Overloading assignment operator in polymorphic classes - c++

I'm coming from java so please bear with me. I've read several other articles and can't seem to find an answer.
I've got a base class (Obj) header file shown below.
class Obj {
public:
Obj();
Obj(int);
int testInt;
virtual bool worked();
Obj & operator = (const Obj & other) {
if(this != &other) {
//other.testInt = this->testInt;
return *this;
}
}
};
Base class
Obj::Obj() {
}
Obj::Obj(int test) {
this->testInt = test;
}
bool Obj::worked() {
return false;
}
Here's the child class header
class Obj1 : public Obj {
public:
Obj1();
Obj1(int);
virtual bool worked();
};
Child class
#include "Obj1.h"
Obj1::Obj1() {
}
Obj1::Obj1(int a) {
this->testInt = a / 2;
}
bool Obj1::worked() {
return true;
}
Here's my main class
int main() {
Obj obj = Obj(99);
Obj1 obj1 = Obj1(45);
obj = obj1;
if(obj.worked())
cout << "good" << obj.testInt << endl;
else cout << "bad " << obj.testInt << endl;
if(obj1.worked()) {
cout << "1good " << obj1.testInt << endl;
} else
cout << "1bad " << obj1.testInt << endl;
return 0;
}
Here's the output when it's ran
bad 99
1good 22
How do I get it so obj = obj1; (found in main above) makes it so that obj.worked() will return true (since that's how obj1's class defines it)? Essentially how do I get it to behave like it would in java? I don't need a deep copy, I just want to toss out what obj used to reference and have it point to obj1 (I think thats how it works in java).

Note: I'm not very familiar with Java.
There's a major difference between "variables" in C++ and Java:
class X { public: int m = 5; };
X a; // no `= X();` required
X b;
a = b;
a.m = 42;
print(b.m); // this line is pseudo-code
In Java, variables may point to different objects. In the example above, after the assignment, a and b point to the same object. Modifying this object through one will make the modification visible when accessing the object through the other, print(b.m) will print 42.
In C++, "variables" (actually: names) always refer to the same object. There are two objects, one named a and one named b, and the assignment doesn't change that. Per default/convention, assignment in C++ means (deep) copy. a = b will be interpreted by most people and in the case of built-in types as copy the contents of b to a (or, more formally, change a such that it will be equal to b afterwards, without altering b).
Now it should be clear that you cannot alter which override of worked will be called by using the assignment in C++: which override of a virtual function is called is selected based on the type of the object (dynamic type), and you cannot change which object a name (variable) refers to.
However, there are pointers in C++, so-called raw pointers and smart pointers. Pointers are objects themselves that point to other objects of one specific type. X* is a raw pointer that points to an object of type X even with polymorphism! Similarly, std::shared_ptr<X> is a smart pointer that points to an object of type X.
std::shared_ptr<X> pa = std::make_shared<X>();
std::shared_ptr<X> pb = std::make_shared<X>();
Every make_shared creates an object. So we have four objects in this example: pa, pb, and the two unnamed objects created via make_shared.
For pointers, there are several operators for dealing with the object pointed to. The most important one is the asterisk, which dereferences the pointer. *pa will give you the object pa points to. The pa-> operator is a shorthand for (*pa)., so you can use it to access members of the object pointed to.
The assignment of pointers does not copy the object pointed to. After the assigment pa = pb, both will point to the same object. For smart pointers, that implies cleaning up objects that are not referred to any more:
std::shared_ptr<X> pa = std::make_shared<X>();
std::shared_ptr<X> pb = std::make_shared<X>();
// 4 objects exist at this point
pa = pb;
// only 3 objects still exist, the one `pa` formerly pointed to was destroyed
Polymorphism in C++ now works with either references (not explained here) or pointers. I said earlier that pointers can only point to one specific type of object. The crux is that this object might be part of a bigger object, e.g. via composition. But inheritance in C++ is very similar to composition: all the members of a base class become part of the base class subobject of a derived class' object:
std::shared_ptr<Obj1> pobj1 = std::make_shared<Obj1>();
std::shared_ptr<Obj> pobj = pobj1;
Here, pobj points to the Obj base class subobject within the object *pobj1 (i.e. within the object pobj1 points to).
Polymorphism now works via virtual functions. Those have a special rule for which function is actually called. The expression *pobj gives us the object which pobj points to, and it is of type Obj. But in this example, it is only a base class subobject, i.e. the object we originally created is of a type derived from Obj. For these cases, we differentiate between the static and the dynamic type of an expression:
The static type of *pobj is always Obj - generally, for an object p, whose type is pointer to some_type, the static type of *p is just some_type, removing one level of indirection / one pointer to.
The dynamic type of *pobj depends on which object pobj currently points to, and therefore generally is not known at compile-time. If the object is a base class subobject, we use the derived class object which it is part of, and recurse until the object we have is not a base class subobject any more. The type of the object we end up with is the dynamic type of the expression. In the example above, pobj points to the Obj base class subobject of *pobj1. The object *pobj1 itself is not a base class subobject here, therefore the dynamic type of *pobj is Obj1.
This dynamic type is now used to select which virtual function override is called. In the case pobj->worked(), where the dynamic type of *pobj is Obj1, the override selected is Obj1::worked, which will return true.
N.B. As Ben Voigt pointed out, the dynamic type does not depend on composition. It is only about inheritance.

In C++, your objects are values not references as in java. The assignment (obj = obj1) will reference to the Obj part of Obj1. In C++ you have to use pointer or reference.
Pointer
Obj* obj = new Obj(99);
Obj1* obj1 = new Obj1(45);
delete obj;// you have to free the memory manually as there's no GC in C++
obj = obj1;
obj->Worked();// the worked will be true here
delete obj1; // manually delete it
and if you want to delete obj1 via obj (delete obj instead of delete obj1), you have to change the Obj's destructor to be virtual, otherwise the destructor of Obj1 won't be called. Damn, this is C++, enjoy it.
reference
Obj obj = Obj(99);
Obj1 obj1 = Obj1(45);
Obj& obj2 = obj1;
obj2.Worked() // should be true
In this case, unlike the pointer, you don't have to delete the objects as they are on stack (not created by 'new'). But you cannot create an array of Obj& (e.g. vector)

Related

Placement new on non-pointer variables and class members

Consider the following example:
#include <iostream>
struct A {
int i;
A(int i)
{
this->i = i;
}
A &operator=(const A &a) = delete;
A(const A &a) = delete;
};
int main()
{
A a(1);
new(&a) A(5);
//a = A(7); // not allowed since = is deleted in A
std::cout << a.i << std::endl;
}
This is a simple example using the placement new operator. Since the copy constructor and assignment operator of struct A have been deleted (for whatever reason), it is not possible to change the object the variable A a holds, except for passing its address to the placement new operator.
Reasons for this might include that struct A holds large arrays (e.g. 100M entries) which would have to be copied in the assignment operator and the copy constructor.
The first part of the question revolves around the "legality" of this approach. I found this stackoverflow question, the accepted answer of which says
this is perfectly legal.
And useless, because you cannot use var [A a in this case] to refer to the state of the [object] you stored within it after the placement new. Any such access is undefined behavior.
[…] under no circumstance may you ever refer to var after you placement new'd over it.
Why would that be the case? I have seen several other examples for the placement new operator, which are always similar to
A a(1);
A *b = new(&a) A(2);
// Now use *b instead of a
From my understanding it should not matter whether A a or A *b is used to access the object since the placement new replaces the object at the address of A a which of course is A a. That is, I would expect that always b == &a. Maybe the answer was not clear enough and this limitation is due to the const-ness of the class member.
Here is another example with the same idea, however this time struct A is embedded into another object:
#include <iostream>
struct A {
int *p;
A(int i)
{
p = new int(i);
}
~A()
{
delete p;
}
A &operator=(const A &a) = delete;
A(const A &a) = delete;
};
struct B {
A a;
B(int i) : a(i)
{
}
void set(int i)
{
a.~A(); // Destroy the old object
new(&a) A(i);
}
};
int main()
{
B b(1);
b.set(2);
std::cout << *(b.a.i) << std::endl;
// This should print 2 and there should be no memory leaks
}
The question is basically the same with the same reasoning. Is it valid to placement-new into the address &a?
For this specific code, you are okay and can use a to refer to the new object that you put in its place. This is covered by [basic.life]/8
If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if:
the storage for the new object exactly overlays the storage location which the original object occupied, and
the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and
the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and
neither the original object nor the new object is a potentially-overlapping subobject ([intro.object]).
emphasis mine
You check off all of those requirements so a will refer to the "new" A that you placed in a's memory.

Manually constructing a trivial base class via placement-new

Beware, we're skirting the dragon's lair.
Consider the following two classes:
struct Base {
std::string const *str;
};
struct Foo : Base {
Foo() { std::cout << *str << "\n"; }
};
As you can see, I'm accessing an uninitialized pointer. Or am I?
Let's assume I'm only working with Base classes that are trivial, nothing more than (potentially nested) bags of pointers.
static_assert(std::is_trivial<Base>{}, "!");
I would like to construct Foo in three steps:
Allocate raw storage for a Foo
Initialize a suitably-placed Base subobject via placement-new
Construct Foo via placement-new.
My implementation is as follows:
std::unique_ptr<Foo> makeFooWithBase(std::string const &str) {
static_assert(std::is_trivial<Base>{}, "!");
// (1)
auto storage = std::make_unique<
std::aligned_storage_t<sizeof(Foo), alignof(Foo)>
>();
Foo * const object = reinterpret_cast<Foo *>(storage.get());
Base * const base = object;
// (2)
new (base) Base{&str};
// (3)
new (object) Foo();
storage.release();
return std::unique_ptr<Foo>{object};
}
Since Base is trivial, my understanding is that:
Skipping the trivial destructor of the Base constructed at (2) is fine;
The trivial default constructor of the Base subobject constructed as part of the Foo at (3) does nothing;
And so Foo receives an initialized pointer, and all is well.
Of course, this is what happens in practice, even at -O3 (see for yourself!).
But is it safe, or will the dragon snatch and eat me one day?
This seems to be explicitly disallowed by the standard.
Ending an objects lifetime, and starting a new objects
lifetime in the same location is explicitly allowed,
unless it's a base class:
§3.8 Object Lifetime
§3.8.7 - If, after the lifetime of an object has ended and before the storage
which the object occupied is reused or released, a new object is
created at the storage location which the original object occupied, a
pointer that pointed to the original object, a reference that referred
to the original object, or the name of the original object will
automatically refer to the new object and, once the lifetime of the
new object has started, can be used to manipulate the new object, if:
the storage for the new object exactly overlays the storage location
which the original object occupied, and
the new object is of the
same type as the original object (ignoring the top-level
cv-qualifiers), and
[snip] and
the original object was a most derived object (1.8) of type T and the
new object is a most derived object of type T (that is, they are not
base class subobjects).

Does reuse storage start lifetime of a new object? [duplicate]

This question already has answers here:
Is it allowed to write an instance of Derived over an instance of Base?
(4 answers)
Closed 8 years ago.
#include <cstdlib>
struct B {
virtual void f();
void mutate();
virtual ~B();
};
struct D1 : B { void f(); };
struct D2 : B { void f(); };
void B::mutate() {
new (this) D2; // reuses storage — ends the lifetime of *this
f(); // undefined behavior - WHY????
... = this; // OK, this points to valid memory
}
I need to be explained why f() invokation has UB? new (this) D2; reuses storage, but it also call a constructor for D2 and since starts lifetime of a new object. In that case f() equals to this -> f(). That is we just call f() member function of D2. Who knows why it is UB?
The standard shows this example § 3.8 67 N3690:
struct C {
int i;
void f();
const C& operator=( const C& );
};
const C& C::operator=( const C& other) {
if ( this != &other ) {
this->~C(); // lifetime of *this ends
new (this) C(other); // new object of type C created
f(); // well-defined
}
return *this;
}
C c1;
C c2;
c1 = c2; // well-defined
c1.f(); // well-defined; c1 refers to a new object of type C
Notice that this example is terminating the lifetime of the object before constructing the new object in-place (compare to your code, which does not call the destructor).
But even if you did, the standard also says:
If, after the lifetime of an object has ended and before the storage
which the object occupied is reused or released, a new object is
created at the storage location which the original object occupied, a
pointer that pointed to the original object, a reference that referred
to the original object, or the name of the original object will
automatically refer to the new object and, once the lifetime of the
new object has started, can be used to manipulate the new object, if:
— the storage for the new object exactly overlays the storage location
which the original object occupied, and — the new object is of the
same type as the original object (ignoring the top-level
cv-qualifiers), and
— the type of the original object is not
const-qualified, and, if a class type, does not contain any non-static
data member whose type is const-qualified or a reference type, and
— the original object was a most derived object (1.8) of type T and the
new object is a most derived object of type T (that is, they are not
base class subobjects).
notice the 'and' words, the above conditions must all be fulfilled.
Since you're not fulfilling all the conditions (you have a derived object in-placed into the memory space of a base class object), you have undefined behavior when referencing stuff with an implicit or explicit use of this pointer.
Depending on the compiler implementation this might or might now blow because a base class virtual object reserves some space for the vtable, in-place constructing an object of a derived type which overrides some of the virtual functions means the vtable might be different, put alignment issues and other low-level internals and you'll have that a simple sizeof won't suffice to determine if your code is right or not.
This construct is very interesting:
The placement-new is not guaranteed to call the destructor of the object. So this code will not properly ensure end of life of the object.
So in principle you should call the destructor before reusing the object. But then you would continue to execute a member function of an object that is dead. According to standard section.9.3.1/2 If a non-static member function of a class X is called for an object that is not of type X, or of a type derived from X, the behavior is undefined.
If you don't explicitely delete your object, as you do in your code, you then recreate a new object (constructing a second B without destoying the first one, then D2 ot top of this new B).
When the creation of your new object is finished, the identity of your current object has in fact changed while executing the function. You cannot be sure if the pointer to the virtual function that will be called was read before your placement-new (thus the old pointer to D1::f) or after (thus D2::f).
By the way, it's exactly for this reason, that there are some constraints about what you can or can't do in a union, where a same memory place is shared for different active objects (see Point 9.5/2 and perticularly point 9.5/4 in the standard).

C++ private constant pointer

I am learning pointers in c++ and am having some trouble.
I have a class Foo that in the header file declares some data:
private:
const Bar *obj;
Where Bar is a class.
Then in the c++ implementation I want to replace *obj so that it points to a completely different Bar object. *obj is constant, so how do I change what is in what *obj points to or rather, what is in memory at *obj? Also in Foo's destructor, how do I deallocate *obj?
Given your class definition
class A {
private:
const Bar *obj;
};
obj is a pointer to a constant Bar object. You can change what that pointer points to, but you cannot change the contents of the object pointed to.
So, if you have a new Bar object and you'd like to change obj so it points to that, you can simply assign the new value:
/* New object: */
Bar *new_bar = new Bar;
/* Change the pointer: */
obj = new_bar;
There are two issues, however.
If the new Bar object is created outside the class, you cannot directly assign it to obj because the latter is private. Hence you need a setter function:
class A {
private:
const Bar *obj;
public:
void set_obj(const Bar *new_obj) { obj = new_obj; }
};
You must determine who will eventually own the Bar object, i.e. who is responsible for freeing the heap space it takes. If the caller is responsible then you can code it as above, i.e. class A will never create new Bar objects, nor delete any. It will just maintain a pointer to Bar objects created and deleted outside the class.
But if class A above is actually responsible for the memory space taken by the Bar objects, you must use delete obj in the destructor to free the space, and you must also free the space when you get a new Bar object assigned. That is, the set_obj function above needs to be changed to this:
void set_obj(const Bar *new_obj) { delete obj; obj = new_obj; }
Otherwise you'll have a memory leak. Similar measures must be taken in the copy constructor (unless you delete it), as well as the assignment operator: Both functions are used whenever a copy of a class A object is made, and in that case you must make sure that you do not simply copy the pointer, but instead allocate fresh space and copy the object (i.e. you must perform a deep copy):
A(const A& a):obj(new Bar(*a.obj)) {}
A& operator=(const A& a) { delete obj; obj = new Bar(*a.obj); return *this; }
Having said this, if your class is responsible for the memory space, it is a much better idea to use a smart pointer class instead of a raw pointer. The main reasons are: (i) The above is quite complicated and it's easy to make mistakes; (ii) The above is still not very good – there may still be memory leaks or worse problems when an exception is thrown, e.g. in the constructor of Bar. C++11 provides a smart pointer class called std::unique_ptr, which seems ideal for your purposes:
class A {
private:
std::unique_ptr<const Bar> obj;
public:
~A() {}
void set_obj(std::unique_ptr<const Bar> new_obj) { obj = new_obj; }
};
With this in place, the smart pointer will take care of any memory space that needs to be freed automatically, both at destruction time as well as when a new Bar object is assigned to the pointer.
You cannot use that pointer to to change the value that's being pointed to, that's why it's a const, but you should be able to change what it is pointing to.
On c++ "const Bar *obj;" means that you have a pointer to a readonly Bar object; meaning that you can point it to any readonly Bar object.
You can also point a non constant variable, thus promising that you will not change it using that pointer.
If you want to have a pointer, that is constant in the sense that it can't be made to point anything else, then you should write it this way:
Bar * const obj = some_object;
This will compile and work fine:
const int p = 1, q = 2;
int r = 3;
const int* i = &p;
i = &q; // point it to a different const variable.
i = &r; // point it to a different non const variable.
As written I believe that this is a pointer to a const Bar object and not a constant pointer.

Proper Coding: When returning a new instance of a class

Let's say I have a class called MyClass, which has two member variables (both being int) called firstNumber_ and secondNumber_. This class also has a method called clone( ) which creates a new instance of the object with the same values and returns that new instance.
The question is, when returning a new instance of the class, do I return it as a pointer, a reference, or just the instance itself?
// MyClass.h
class MyClass {
private:
int firstNumber_;
int secondNumber_;
public:
MyClass( int numA, int numB ) {
firstNumber_ = numA;
secondNumber_ = numB;
}
~MyClass( ) { };
// This method creates a copy of the object and returns that object.
// The ( ? ) is there because I am not sure what type the returned value is.
MyClass ( ? ) clone( ) {
// Do I just return the new instance? Or do I need to return a reference or a pointer of the new instance?
return MyClass( firstNumber_, secondNumber_ );
}
};
What's the proper way of achieving this? Note that MyClass does not inherit from any other classes.
Thanks in advance.
The method clone() is usually for returning a pointer to an instance of a polymorphic type, so you can call it on a pointer of the base type and get an pointer to an instance of the right derived type. For simple copying, in C++ you use the copy constructor, not a clone method.
Clone method:
struct Base{};
struct Foo : Base
{
Base* clone() const { return new Foo(*this); }
}:
Base * b1 = new Foo;
...
Base * b2 = b1->clone(); // points to a Foo instance, copy of object pointed at by b1
Where the clone method us using the (compiler generated) copy constructor.
Using copy constructor:
Foo f1;
Foo f2 = f1; // copy assignment
In real code you should prefer smart pointers over the raw ones used in these examples.
Concerning the return value, by default you should return by value, although in the polymorphic clone() example this would defeat the purpose by slicing the return object into a Base. So we return a pointer to an instance allocated dynamically. The intent and ownership has to be clear: in this case, the caller owns the pointer, and is responsible for deallocating it. Pre-c++11 you would have to rely on documentation to convey this. In C++11 you can use the smart pointer std::unique_ptr, which enforces the ownership and states it clearly.