Let us assume I have an Object MeasurementValues, which has n different pointers (this examples just show pointers to primitive types, but pointers to other complex objects mivght occur as well).
class MesaurementValues {
private:
int *measurement_1;
double *measurement_2;
long long *measurement_3;
//..
float *measurement_n;
int noPointer;
}
I know, this example might be a little bit contrived, anyway. I try to fullfill the Rule of Five in my code.
Do I have to
this.measurement_x = old.measurement_x ;// for all x = {1,..,n} ?
First off just be aware that move-semantics give no advantage over copy-semantics for plain-old-data type (POD) members, but it sure does when your class contains other class objects and/or arrays. When you implement move-semantics, it means you have a "move constructor" and/or a "move assignment operator" which would look like this in your class:
class MesaurementValues { private:
int *measurement_1;
double *measurement_2;
long long *measurement_3;
//..
float *measurement_n;
int noPointer;
//a couple different objects
someObject* pObj1;
differentObject* pObj2;
public:
MeasurementValues( MeasurementValues&& move ); //move-constructor
MeasurementValues& operator= (MeasurementValues&& move); //move-assignment
}
-Assuming your class has POD data and class objects, and
-assuming there are alot of variables to move over:
MeasurementValues::MeasurementValues( MeasurementValues&& old) {
//copy plain-old-data over
measurement_1 = old.measurement_1;
measurement_2 = old.measurement_2;
//copy over values of the pointers
pObj1 = old.pObj1;
pObj2 = old.pObj2;
}
Keep in mind that move-semantics, as other posters have said, only have an advantage if your data members are other moveable objects, or dynamically-allocated memory.
EDIT:
pointers in must become "invalid", as they've been moved. Therefore I would set them to null to prevent unexpected behavior:
MeasurementValues::MeasurementValues( MeasurementValues&& old)
: measurement_1() //null...
//,...
{
//Swapping null into old...
std::swap(measurement_1, old.measurement_1);
//...
}
Do I have to
this.measurement_x = old.measurement_x ;// for all x = {1,..,n} ?
I would rely on the pimpl idiom for this, and use something like a unique pointer. Below is a detailed example. Note you only have to work with Impl, and rely on its defaults (as it contains no pointers).
#include <iostream>
#include <memory>
struct Moveable
{
public:
Moveable();
~Moveable();
Moveable(const Moveable& m);
Moveable& operator=(const Moveable& m);
Moveable(Moveable&& m);
Moveable& operator=(Moveable&& m);
int foo() const;
private:
struct Impl;
std::unique_ptr<Impl> pimpl_;
};
//Moveable.cpp
struct Moveable::Impl
{
Impl(): a(1), b(2), c(3), buffer(){}
int a, b, c;
char buffer[10000]; //Make it worth our while...
};
int Moveable::foo() const{ return pimpl_->a+pimpl_->b+pimpl_->c;}
Moveable::Moveable()
: pimpl_(new Impl)
{
std::cout << "Default " << (void*)this << std::endl;
}
Moveable::~Moveable()
{
std::cout << "Destruct " << (void*)this << std::endl;
//automagically...
}
Moveable::Moveable(const Moveable&m)
: pimpl_(new Impl(*m.pimpl_))
{
std::cout << "Copying " << &m << " to " << (void*)this << std::endl;
}
Moveable& Moveable::operator=(const Moveable& m)
{
std::cout << "Copy assign " << (void*)&m << " to " << (void*)this << std::endl;
*pimpl_ = *m.pimpl_; //Relying on their defaults...
}
Moveable::Moveable(Moveable&& m)
: pimpl_(move(m.pimpl_))
{
std::cout << "Moving " << (void*)&m << " to " << (void*)this << std::endl;
}
Moveable& Moveable::operator=(Moveable&& m)
{
pimpl_ = move(m.pimpl_);
std::cout << "Move assigning " << &m << " to " << (void*)this << std::endl;
}
int main()
{
Moveable x;
Moveable y(x); //Copying...
y = x; //Copying assign
y = Moveable(); //Default construct, then move assignment, destruct temp
Moveable z(std::move(y));
std::cout << "Calling foo " << z.foo() << std::endl;
return 0;
}
Related
How can a vector of objects and a single object be iterated over as-if they were the same range, without any copying or moving, in range-v3?
One possibility is to cast the single value to a std::array<Object, 1> and then concatenate this array with the vector of objects via range::views::concat:
Improvised solution
#include <array>
#include <iostream>
#include <range/v3/view/concat.hpp>
struct Object {
int value = 0;
Object() { std::cout << "Default " << value << "\n"; }
Object( int value) : value(value) { std::cout << "Value " << value << "\n"; }
Object(Object const& object) : value(object.value) { std::cout << "Copy " << value << "\n"; }
Object(Object && object) : value(std::move(object.value)) { std::cout << "Move " << value << "\n"; }
};
int main() {
// Prints "Value 0"
std::array<Object, 1> objects {0};
// Prints "Value 1"
Object object {1};
for(Object const& object : ranges::views::concat(objects, *reinterpret_cast<std::array<Object, 1>*>(&object))) {
// Prints "0 1"
std::cout << object.value << " ";
}
return 0;
}
I don't know if the cast is safe. Assuming it is, developers are at risk of having to spend time to verify this. Hence this improvised solution is wanting.
Question: Is there an elegant solution for concatenating a view and a single value without copying or moving data?
I am aware of range::views::single but this causes object to be copied once and moved thrice when used in place of reinterpret_cast in the improvised solution.
Now I need to detect whether my class is created as a stack/global/thread_local variable, for example:
class Foo {
public:
Foo() {
if(im_on_stack) {
std::cout << "I'm on stack" << std::endl;
} else if(im_in_global) {
std::cout << "I'm in global" << std::endl;
} else if(im_a_thread_local) {
std::cout << "I'm a thread_local" << std::endl;
} else {
std::cout << "I'm on ohters location" << std::endl;
}
}
};
class Bar {
Foo mFoo;
};
Foo gFoo;
thread_local Foo tFoo;
int main() {
Foo lFoo;
}
and the out put should be:
I'm on ohters location
I'm in global
I'm a thread_local
I'm on stack
This there any way in C++ I can do this?
Edit:
why I'm doing this:
I'm writing a garbage collection library, and I got a class, let's call it gc_ptr, I need to know if this gc_ptr is a gc root (which is create on the location I mentioned) or not (which is a member of another class)
Edit2:
According to the concept of gc root, which is a reference which is not on a heap, I should probably asked in this way: can I detect if my class is create on the heap? But I think on heap or on stack make this question no difference.
Short answer: No. Not with standard C++. There may be compiler or OS specific solutions, but nothing portable.
I guess you could make a heuristic to detect stack allocated objects by detecting in their constructor whether their address is close to a stack allocated variable. Assuming that the stack and the heap have completely different memory addresses it should work. Completely undefined behaviour though according to the standard. e.g.:
#include <iostream>
#include <memory>
struct A
{
A()
{
int test = 0; // test is on the stack
auto distance = reinterpret_cast<char*>(this) - reinterpret_cast<char*>(&test);
isStack = std::abs(distance) < 1024; // completely arbitrary magic number, will need to experiment
}
bool isStack;
};
int main()
{
std::cout << "stack: " << A().isStack << "\n";
std::cout << "stack: " << std::make_unique<A>()->isStack << "\n";
}
I don't think you could expand this technique to thread local variables. You'd also need to take care in copy constructors and assignment operators to handle copying from a stack to a heap object and vice versa.
This is not part of the spec, so no.
If you told us on what system/os you are working, maybe we can assist - some systems may provide the stack address and size - usually this is available in embedded devices as part of the compiler output (the address), and as input (the size) as part of the environment/project configuration.
What I wrote in my comment:
Can be done to some extent: Assuming, you need it only for a certain kind of classes: You have to overload new (or wrap dynamic construction in a static create). You have to derive all considered classes from a base class with a specific constructor. To pass info from new to the constructor is a bit tricky. In our case, we used a global set where new remembered pointers to created instances and the corresponding constructor looked into it to determine whether creation was done by new. That was sufficient for us. (About the other topics - no idea...)
A demo:
#include <iostream>
#include <set>
class Object {
private:
static thread_local std::set<void*> pNewSet;
bool _isNewed;
public:
Object();
Object(const Object&) = delete;
const Object& operator=(const Object&) = delete;
~Object() = default;
static void* operator new(size_t size);
bool isNewed() const { return _isNewed; }
private:
static std::set<void*>& getNewPtrs()
{
static thread_local std::set<void*> pNewSet;
return pNewSet;
}
};
void* Object::operator new(size_t size)
{
std::set<void*> &pNewSet = getNewPtrs();
void *p = ::operator new(size);
if (p) pNewSet.insert(p);
return p;
}
Object::Object(): _isNewed(false)
{
std::set<void*> &pNewSet = getNewPtrs();
std::set<void*>::iterator iter = pNewSet.find((void*)this);
if (iter != pNewSet.end()) {
_isNewed = true;
pNewSet.erase(iter);
}
}
#define DEBUG(...) std::cout << #__VA_ARGS__ << ";\n"; __VA_ARGS__
// a global static instance
static Object o;
int main()
{
DEBUG(std::cout << o.isNewed() << '\n');
// a static instance (local scope)
DEBUG(static Object o1);
DEBUG(std::cout << o1.isNewed() << '\n');
// a local instance
DEBUG(Object o2);
DEBUG(std::cout << o2.isNewed() << '\n');
// as members
DEBUG(struct Composed { Object o1, o2; } comp);
DEBUG(std::cout << comp.o1.isNewed() << ' ' << comp.o2.isNewed() << '\n');
// created with new
DEBUG(Object *pO = new Object());
DEBUG(std::cout << pO->isNewed() << '\n');
DEBUG(delete pO);
// created as members in an object created with new
DEBUG(Composed *pComp = new Composed());
DEBUG(std::cout << pComp->o1.isNewed() << ' ' << pComp->o2.isNewed() << '\n');
DEBUG(delete pComp);
}
Output:
std::cout << o.isNewed() << '\n';
0
static Object o1;
std::cout << o1.isNewed() << '\n';
0
Object o2;
std::cout << o2.isNewed() << '\n';
0
struct Composed { Object o1, o2; } comp;
std::cout << comp.o1.isNewed() << ' ' << comp.o2.isNewed() << '\n';
0 0
Object *pO = new Object();
std::cout << pO->isNewed() << '\n';
1
delete pO;
Composed *pComp = new Composed();
std::cout << pComp->o1.isNewed() << ' ' << pComp->o2.isNewed() << '\n';
0 0
delete pComp;
Live Demo on Compiler Explorer
Notes:
The copy constructor and assignment operator of Object are deleted intenionally.
Derived classes may provide a copy constructor but it has to call Object::Object().
The sample doesn't consider other flavors of new (e.g. operator new[] or the align variants since C++17). That should be done in productive code.
No, but you can prevent creation of objects on heap/stack.
To prevent creation on stack make the destructor private/protected:
class heap_only
{
public:
void release() const { delete this; }
protected:
~heap_only() {}
};
struct heap_only_deleter
{
void operator()( heap_only* p ) { p->release(); }
};
using up_heap_only = std::unique_ptr<heap_only, heap_only_deleter>;
//...
heap_only ho; // fails
heap_only* pho = new heap_only();
pho->release();
up_heap_only up{ new heap_only() };
To prevent creation on heap make the new operators private/protected:
class stack_only
{
protected:
static void* operator new( std::size_t );
static void* operator new[]( std::size_t );
};
//...
stack_only* pso = new stack_only(); // fails
stack_only so;
Regarding your edits, and just for fun:
void* pstart;
bool is_on_stack( void* p )
{
int end;
return pstart >= p && p > &end;
}
int globalint;
int main()
{
//
int start;
pstart = &start;
//
int stackint;
std::cout << std::boolalpha
<< is_on_stack( &globalint ) << std::endl
<< is_on_stack( &stackint ) << std::endl
<< is_on_stack( new int );
//...
}
I have two classes, let's call them A and B
class A:
{
public:
//Some functions
A *getNewA() const;
private:
//some attributes
}
class B:
{
public:
//Some functions
private:
A &reftoA;
}
In the main code, I must generate a new A thanks to the A::getNewA() method. And this must go to B::reftoA, as written in class B.
Here is the A::getNewA() method :
A *A::getNewA()
{
A *newA = new A;
return newA;
}
OK. So now I call getNewA and want to store the results in reftoA, which is a reference to A. In a B function (which take a reference to A as parameter)
B::foo(A ¶mA)
{
reftoA = *(paramA.getNewA());
}
I thought this should have been working, but it won't.
Because when dereferencing, reftoA will always take the this object and not the new allocated object.
Let's be clearer and let's modify the functions to output the results
A * A::getNewA()
{
A *newA = new A;
std::cout << "New pointer " << newA << std::endl;
std::cout << "this pointer" << this << std::endl;
return A;
}
void B::foo(A ¶mA)
{
reftoA = *(paramA.getNewA());
std::cout << "new generated pointer " << &reftoA << std::endl;
}
Here is one of the output :
New pointer : 004FFAEC
this pointer: 0069D888
New generated pointer : 0069D888 //Expected : 004FFAEC
I can't get this "new generated pointer" to be the same than the new pointer the A::getNewA() returns after having allocated the memory. Of course, I guess there is some point with dereferencing the pointer to store it in a reference.
I know reference are used with existing object. Maybe the new object A::getNewA() should allocate memory for won't work as I expected.
I could use pointer instead reference in B::foo(), I know, but I can't
I think I am misunderstanding something about refrence and pointer, but I don't know what.
Any help greatly appreciated
The problem is that you can not reassign a reference. You can change only the value of the referenced object.
So you have to initialize the reference in the initializer list of the constructor of the class B.
Take into account that there is a typo in your code snippet
A*A::getNewA()
{
A *newA = new A;
std::cout << "New pointer " << newA << std::endl;
std::cout << "this pointer" << this << std::endl;
return A;
^^^^^^^^^
}
I think you mean
A*A::getNewA() const
^^^^^
{
A *newA = new A;
std::cout << "New pointer " << newA << std::endl;
std::cout << "this pointer" << this << std::endl;
return newA;
^^^^^^^^^^^
}
Always try to provide a verifiable complete example.
Here is a demonstrative program
#include <iostream>
class A
{
public :
//Some functions
A* getNewA() const
{
A *newA = new A;
std::cout << "New pointer " << newA << std::endl;
std::cout << "this pointer" << this << std::endl;
return newA;
}
private :
//some attributes
};
class B
{
public :
B( const A& a ) : reftoA( *a.getNewA() )
{
std::cout << "&reftoA " << &reftoA << std::endl;
}
private :
A& reftoA;
};
int main()
{
A a;
B b( a );
return 0;
}
Its output is
New pointer 0x2b392afbec20
this pointer0x7ffd287ad0af
&reftoA 0x2b392afbec20
As you can see the values of the New pointer and &reftoA are equal each other.
To make it more clear consider a very simple example
#include <iostream>
int main()
{
int x = 10;
int y = 20;
int &r = x;
r = y;
std::cout << "x = " << x << std::endl;
std::cout << "y = " << y << std::endl;
std::cout << "r = " << r << std::endl;
std::cout << std::endl;
std::cout << "&x = " << &x << std::endl;
std::cout << "&y = " << &y << std::endl;
std::cout << "&r = " << &r << std::endl;
return 0;
}
The program output is
x = 20
y = 20
r = 20
&x = 0x7ffd88ad47a8
&y = 0x7ffd88ad47ac
&r = 0x7ffd88ad47a8
This statement
r = y;
did not force the reference to refer the object y. It just reassigned the value of the referenced object x.
References have to be initialized when they are created.
Yes, you are misunderstanding something.
getNewA() is returning a pointer. it's not a smart pointer, you want to look into those and that's all I'll say on the matter.
on returning a pointer, you must keep a reference to this pointer else you will be unable to delete it and you'll get a memory leak. Thus you MUST have somewhere A* a = A::getNewA() and then later, when you no longer need it delete a;
Where you need to pass a reference to A, you can do foo(*a) which will dereference the pointer and pass a reference to the object it's pointing to.
But in summary, for all new code, smart pointers; there's no excuse to not use them.
Side note: Your code example had a few other issues; such as getNewA wasn't static; I'm going to take the code as a working example of your understanding, and not a working example.
Edit: On re-reading your example, the getNewA is intentionally non-static. I think this question is actually an XY problem (ie you're asking a question you've forced yourself into but isn't your actual problem); but I hope this addresses your misunderstanding of pointers and references.
You are not returning the pointer in the getNewA-Method
A* A::getNewA()
{
A *newA = new A;
return A; // you are returning A and not newA
}
And if you want to reassign the reference to a you can use a std::reference_wrapper
class B :
{
public :
void foo(A& paramA) {
reftoA = *(paramA.getNewA());
}
private :
std::reference_wrapper<A> reftoA;
}
Suppose I have the following code in C++:
#include <memory>
#include <iostream>
struct Some {
Some(int _a) : a(_a) {}
int a;
};
int main() {
Some some(5);
std::unique_ptr<Some> p1 = std::make_unique<Some>(some);
std::unique_ptr<Some> p2 = std::make_unique<Some>(some);
std::cout << p1->a << " " << p2->a << std::endl;
return 0;
}
As I understand, unique pointers are used to guarantee that resources are not shared. But in this case both p1 and p2 point to the same instance some.
Please unveil the situation.
They don't point to the same resource, they each point to a different copy of it. You can illustrate it by deleting the copy constructor to see the error:
#include <memory>
#include <iostream>
struct Some {
Some(int _a) : a(_a) {}
Some(Some const&) = delete;
int a;
};
int main() {
Some some(5);
std::unique_ptr<Some> p1 = std::make_unique<Some>(some); //error here
std::unique_ptr<Some> p2 = std::make_unique<Some>(some);
std::cout << p1->a << " " << p2->a << std::endl;
return 0;
}
std::make_unique creates objects, calling constructor with specified arguments.
You passed Some& as parameter, and here copy constructor was invoked, and new object constructed.
So, p1 and p2 are 2 absolutely different pointers, but constructed from same object, using copy constructor
both p1 and p2 point to the same instance some
No, they don't.
#include <memory>
#include <iostream>
struct Some {
Some(int _a) : a(_a) {}
int a;
};
int main() {
Some some(5);
std::unique_ptr<Some> p1 = std::make_unique<Some>(some);
std::unique_ptr<Some> p2 = std::make_unique<Some>(some);
std::cout << p1->a << " " << p2->a << std::endl;
p1->a = 42;
std::cout << p1->a << " " << p2->a << std::endl;
return 0;
}
output:
5 5
42 5
To test whether two pointers point to the same object instance, you should compare the locations they point to, instead of the object member variables:
std::cout << &(*p1) << " " << &(*p2) << std::endl;
Which will show that they indeed do not point to the same instance.
ADDENDUM: As pointed out by Remy Lebeau, since C++11 it is advisable to use the
std::addressof function for this purpose, which obtains the actual address of an object even if the & operator is overloaded:
std::cout << std::addressof(*p1) << " " << std::addressof(*p2) << std::endl;
I'm using virtual inheritance as in the typical diamond problem:
A
(virtual) / \ (virtual)
B C
\ /
D
I'm implementing a method named "deep_copy_from" in every class (but it could be the assignment operator=() as well). The method should copy the class own attributes, and propagate the copy to the classes above.
The problem is that the A::deep_copy_from method is called twice when I'm deep copying a D instance (and it should be called only once, as there is only one "version" of A). What is the best way to ensure it will be called only once?
(B::deep_copy_from and C::deep_copy_from should continue working the same way).
Here is a sample code:
class A
{
public:
A(string const& p_a_name) : a_name(p_a_name) {
cout << "A(a_name=\"" << p_a_name << "\")" << endl;
}
virtual void deep_copy_from(A const& a)
{
cout << "A::deep_copy_from(A(a_name=\"" << a.a_name << "\"))" << endl;
this->a_name = a.a_name;
}
protected:
string a_name;
};
class B : public virtual A
{
public:
B(string const &p_a_name, string const& p_b_name) : A(p_a_name), b_name(p_b_name) {
cout << "B(a_name=\"" << p_a_name << "\", b_name=\"" << p_b_name << "\")" << endl;
}
virtual void deep_copy_from(B const& b)
{
cout << "B::deep_copy_from(B(a_name=\"" << b.a_name << "\", b_name=\"" << b.b_name << "\"))" << endl;
this->A::deep_copy_from(static_cast<A const&>(b));
this->b_name = b.b_name;
}
protected:
string b_name;
};
class C : public virtual A
{
public:
C(string const &p_a_name, string const& p_c_name) : A(p_a_name), c_name(p_c_name) {
cout << "C(a_name=\"" << p_a_name << "\", c_name=\"" << p_c_name << "\")" << endl;
}
virtual void deep_copy_from(C const& c)
{
cout << "C::deep_copy_from(C(a_name=\"" << c.a_name << "\", c_name=\"" << c.c_name << "\"))" << endl;
this->A::deep_copy_from(static_cast<A const&>(c));
this->c_name = c.c_name;
}
protected:
string c_name;
};
class D : public B, public C
{
public:
D(string const &p_a_name, string const& p_b_name, string const& p_c_name, string const& p_d_name)
: A(p_a_name), B(p_a_name, p_b_name), C(p_a_name, p_c_name), d_name(p_d_name)
{
cout << "D(a_name=\"" << p_a_name << "\", b_name=\"" << p_b_name
<< "\", c_name=\"" << p_c_name << "\", d_name=\"" << p_d_name << "\")" << endl;
}
virtual void deep_copy_from(D const& d)
{
cout << "D::deep_copy_from(D(a_name=\"" << d.a_name << "\", b_name=\"" << d.b_name
<< "\", c_name=\"" << d.c_name << "\", d_name=\"" << d.d_name << "\"))" << endl;
this->B::deep_copy_from(static_cast<B const&>(d));
this->C::deep_copy_from(static_cast<C const&>(d));
this->d_name = d.d_name;
}
protected:
string d_name;
};
Here is the current output:
A(a_name="A")
B(a_name="A", b_name="B")
C(a_name="A", c_name="C")
D(a_name="A", b_name="B", c_name="C", d_name="D")
D::deep_copy_from(D(a_name="A", b_name="B", c_name="C", d_name="D"))
B::deep_copy_from(B(a_name="A", b_name="B"))
A::deep_copy_from(A(a_name="A"))
C::deep_copy_from(C(a_name="A", c_name="C"))
A::deep_copy_from(A(a_name="A"))
Update:
The current version is now:
class A
{
public:
A(string const& p_a_name) : a_name(p_a_name) {
cout << "A(a_name=\"" << p_a_name << "\")" << endl;
}
virtual void deep_copy_from(A const& a)
{
cout << "A::deep_copy_from(A(a_name=\"" << a.a_name << "\"))" << endl;
this->a_name = a.a_name;
}
protected:
string a_name;
};
class B : public virtual A
{
public:
B(string const &p_a_name, string const& p_b_name) : A(p_a_name), b_name(p_b_name) {
cout << "B(a_name=\"" << p_a_name << "\", b_name=\"" << p_b_name << "\")" << endl;
}
virtual void deep_copy_from(B const& b)
{
cout << "B::deep_copy_from(B(a_name=\"" << b.a_name << "\", b_name=\"" << b.b_name << "\"))" << endl;
this->A::deep_copy_from(static_cast<A const&>(b));
this->deep_copy_my_bits_from(b);
}
protected:
void deep_copy_my_bits_from(B const& b) {
cout << "B::deep_copy_my_bits_from(B(a_name=\"" << b.a_name << "\", b_name=\"" << b.b_name << "\"))" << endl;
this->b_name = b.b_name;
}
protected:
string b_name;
};
class C : public virtual A
{
public:
C(string const &p_a_name, string const& p_c_name) : A(p_a_name), c_name(p_c_name) {
cout << "C(a_name=\"" << p_a_name << "\", c_name=\"" << p_c_name << "\")" << endl;
}
virtual void deep_copy_from(C const& c)
{
cout << "C::deep_copy_from(C(a_name=\"" << c.a_name << "\", c_name=\"" << c.c_name << "\"))" << endl;
this->A::deep_copy_from(static_cast<A const&>(c));
this->deep_copy_my_bits_from(c);
}
protected:
void deep_copy_my_bits_from(C const& c) {
cout << "C::deep_copy_my_bits_from(C(a_name=\"" << c.a_name << "\", c_name=\"" << c.c_name << "\"))" << endl;
this->c_name = c.c_name;
}
protected:
string c_name;
};
class D : public B, public C
{
public:
D(string const &p_a_name, string const& p_b_name, string const& p_c_name, string const& p_d_name)
: A(p_a_name), B(p_a_name, p_b_name), C(p_a_name, p_c_name), d_name(p_d_name)
{
cout << "D(a_name=\"" << p_a_name << "\", b_name=\"" << p_b_name
<< "\", c_name=\"" << p_c_name << "\", d_name=\"" << p_d_name << "\")" << endl;
}
virtual void deep_copy_from(D const& d)
{
cout << "D::deep_copy_from(D(a_name=\"" << d.a_name << "\", b_name=\"" << d.b_name
<< "\", c_name=\"" << d.c_name << "\", d_name=\"" << d.d_name << "\"))" << endl;
this->A::deep_copy_from(static_cast<A const&>(d));
this->B::deep_copy_my_bits_from(static_cast<B const&>(d));
this->C::deep_copy_my_bits_from(static_cast<C const&>(d));
this->d_name = d.d_name;
}
protected:
string d_name;
};
And the output is:
A(a_name="A")
B(a_name="A", b_name="B")
C(a_name="A", c_name="C")
D(a_name="A", b_name="B", c_name="C", d_name="D")
D::deep_copy_from(D(a_name="A", b_name="B", c_name="C", d_name="D"))
A::deep_copy_from(A(a_name="A"))
B::deep_copy_my_bits_from(B(a_name="A", b_name="B"))
C::deep_copy_my_bits_from(C(a_name="A", c_name="C"))
Can I get anything better than that? (i.e., more automatic)
#Alf is right about assignment: assignment is screwed. The reason is that it is a binary operation and it is impossible to dispatch binary operations in an OO framework due to the covariance problem.
Now, there is a general answer to your question, but first there are two things you need to know. The first is that virtual bases are always public, no matter what you declare and no matter what the Standard says: the Standard is just wrong. [Proof: just derive another class and declare any virtual base public virtual again, and you have access]
The second fact is that virtual bases are direct bases of every class they're indirect bases of. Again, ignore the Standard, because it is wrong. See above.
Given these two facts, it is easy to see the proper pattern to avoid duplication:
Here's your diamond:
struct A { cp(){ "A" } virtual CP(){ cp(); } };
struct B : virtual A { cp(){ "B" } CP() { cp(); A::CP(); } };
struct C : ... ibid ...
struct D : B, C, virtual A {
cp() { "D"; B::cp(); C::cp(); }
CP() { cp(); A::cp(); }
};
I left off return types and other stuff for brevity. The cp() function
drills down by first processing any members then calling on each non-virtual
base to process its members (recursively). Actually it should be protected,
since it isn't for public clients. The drilling down is mandatory because you can't access indirect non-virtual bases yourself, only direct ones.
The CP() function is virtual, so any call goes to the complete objects unique
CP no matter which pointer (A, B, C, or D) you're accessing the diamond with.
It processes all the members and non-virtual base subobject members by calling cp()
of its own class, then it processes the virtual bases, in this case there is only
one, namely A.
If X::CP() becomes
X *X::clone() const;
then if you can clone the complete object off any pointer and get back the same dynamic and static types: if your dynamic type is D and static type is B, you will get a B* to a D object exactly as you started with.
It is NOT possible to do assignment this way. It is NOT possible to make assignment work at all. The reason is that assignment is covariant on two arguments. There's no way to ensure the source and target have the same dynamic type, and that is necessary for assignment to work. If the source is too big, some of it gets sliced off. Far worse, if the target is too big, some of it never gets assigned to. Therefore it makes no difference which object (the target or source) you dispatch on: it just can't work. The only kind of assignment that can work is a non-virtual one based on static type. That can over or under slice as well, but at least the issue is statically evident.
Cloning works because it is a function with only one argument (namely, the self-object). Generally, if you're using "object stuff" as opposed to "value stuff" then, since you can only really manipulate values, you have to use pointers. In that case clone() and friends are just what you want: you can assign a pointer just fine!
There are two problems: the one about double copying to A part, and the one about virtual assignment op.
Virtual assignment: not a good idea, because it transfers error detection from compile time to run time. Simply don't. The general solution where virtual assignment (or assignment-like op like yours) seems to be required, is to implement cloning instead, a virtual clone member function that produces a dynamically allocated copy.
Double copying: the simple answer is to express assignment in terms of construction. The idiomatic way to do this is known as the "swap idiom". Simply put, construct a copy, then swap its contents with current instance, then let destructor of the instance you constructed take care of cleanup.
Cheers & hth.,
deep_copy_from is not co-variant, you can only use covariance in return types.
You may get "overload hides virtual function" warnings with the code you have written.
As it is there is no way to call the B or C version without invoking the A version so unless you modify B or C you cannot avoid the A version getting called twice if you have to call both the B and C version.
Given that B and C both inherit virtual from A you should probably not get them to invoke the A version as the final class is responsible for the A part.