I have an object that is composed of references to other objects, like so:
class ComposedObject
{
public:
ComposedObject(Object1& obj1, Object2& obj2) : obj1_{obj1), obj2_{obj2} {};
private:
Object1& obj1_;
Object2& obj2_;
}
I would like to create a simple builder like so:
Build_Object1 should be using copy elision so it's not copying objects around.
// this is fine because it uses copy elision
Object1 Build_Object1()
{
auto obj = Object1();
// do some stuff
return obj; // uses copy elision
}
// similar function for object 2
ComposedObject Build_Composed()
{
// this is not fine because it's destroyed
auto obj1 = Build_Object1();
auto obj2 = Build_Object2();
auto composed = ComposedObject(obj1, obj2) ;
return composed;
}
Of course the problem here is that composed is storing references, so when build Build_Composed, obj1 and obj2 are removed from the stack and thus the addresses passed to ComposedObject now contains garbage ( I checked).
I was thinking to maybe use std::move but I believe that requires changing the signature of ComposedObject to use Object1&&.
My options (as I understand them):
use Object1&& and Object2&& in the constructor of ComposedObject
use a smart pointer.
some what other option?
Related
Why when let's say I have an object declared like this: Obj o1; which is initialized by the default constructor (not very important here, how was o1 initialized, the point is it was initialized) and I create another object in this manner: Obj o2 = o1; the copy constructor is implicitly called, but if I delete the copy constructor, then, I get a compile error. Why the object o1 is not assigned/copied to o2, like here: Obj o1; Obj o2; o2 = o1;? Why the compiler tries to call a constructor in any instance? Is the = operator within Obj o2 = o1; overloaded?
As mentioned in comments, this
Obj o2 = o1;
has nothing to do with assignment. It is a little unfortunate, often confusing, use of = for initialization when otherwise = means assignment.
Also mentioned in comments, the operator= has to assume that the left operator already exists. Consider this somewhat contrived example:
#include <vector>
struct my_vect {
my_vect() : data(2) {}
my_vect(const my_vect& other) : data(other.data) {}
my_vect& operator=(my_vect& other) {
// this has already been constructed,
// hence data.size() is 2 already
data[0] = other.data[0];
data[1] = other.data[1];
return *this;
}
private:
std::vector<int> data;
};
It is a structure that contains a std::vector whose size is always 2. The vector is initialized upon constructing a my_vect. When assigning one my_vect to another, then data needs not be initialized. Only the values must be copied. Because operator= assumes that the left hand side operator is already properly constructed (it merely copies to data[0] and data[1]) it cannot possibly be used to construct an object (in the example, accessing data[0] or data[1] would be out of bounds).
TL;DR: Constructors construct objects. Assignment assigns to an already existing object. Thats two fundamentally different things.
I am an beginner in C++ and would like to do something like this:
myObj f(){
// do stuff
// return instance of myObj
}
int main(){
// do stuff
myObj mO = f();
}
What would I have to do, to make this work in C++?
My thoughts were, that I would have to implement an assign operator for the myObj stuct/class or to write another constructor that looks like this myObj::myObj(myObj mO){...}, which I use like this myObj = myObj(f());.
Is this correct?
Do I have to do more, to make this work?
Could you maybe provide a working example?
Thanks!
That would almost compile as-is.
//define a class
class myObj {};
// return an instance of the class
myObj f() {
return myObj{};
}
// call with the same main as in the question:
int main(){
// do stuff
myObj mO = f();
}
C++ defines for you a copy constructor, an assignment operator and a move constructor if this can be done trivially; in these cases you should have to do nothing, just return an object instance and the caller will get it.
If the object has however some parts that cannot be copied (for example references) then you need to provide copy constructors and assignment yourself (but may be the class indeed shouldn't be copied or assigned).
There are also other limitations that prevent automatic synthesis of the move constructor (to avoid bugs).
Note also that there are cases in which the C++ compiler will synthesize copy constructor and assignment, but using wrong code. You need to be careful (for example if the class contains naked owning pointers).
For a simple case in which everything works out of the box with no need to do anything consider:
// A bi-dimensional point
struct P2d {
double x, y;
};
// Computes the middle point given two points
P2d average(P2d a, P2d b) {
return P2d{(a.x+b.x)/2, (a.y+b.y)/2};
}
As you see nothing is needed in the class to support returning P2d values or accepting P2d parameters.
The compiler in that case automatically completes the definition code to something like:
struct P2d {
double x, y;
P2d(const P2d& other)
: x(other.x), y(other.y)
{
}
P2d& operator=(const P2d& other) {
x = other.x;
y = other.y;
return *this;
}
~P2d() {
}
};
Current Implementation
I have a class containing unique_ptr fields which depend on one other:
class ResourceManager {
ResourceManager() {}
ResourceManager(A* a_ptr) :
b_ptr(new B(a)),
c_ptr(new C(b_ptr.get())) {}
ResourceManager& operator=(ResourceManager&& that) {
// Call destructor, then construct a new instance on top
~ResourceManager();
ResourceManager* new_this = new(this) ResourceManager();
// Surely this must be the case, right?
// Is there any reason to prefer using either?
assert(new_this == this);
new_this->b_ptr = that.b_ptr;
new_this->c_ptr = that.c_ptr;
return *new_this;
}
unique_ptr<B> b;
unique_ptr<C> c;
};
Use case
The use case here is that I would like to reassign new values to the pointers, whilst keeping the ResourceManager as a stack-allocated variable, or as a non-pointer class member.
With my current setup I imagine using it something like this:
A a, another_a;
ResourceManager r(&a);
// Use r...
// Destroy old ResourceManager and create the new one in place.
r = ResourceManager(&another_a);
The reason this is even a problem is due to the fact that B and C are non-assignable (for e.g. file streams)
Ugly Alternative
An alternative uglier (and dangerous) method would be to explicitly reset the unique_ptr fields crucially in reverse order (remember that C depends on B, and hence must be destructed first), effectively mimicking the default destruction behaviour.
ResourceManager& operator=(ResourceManager&& that) {
// Mimic destructor call (reverse-order destruction)
c_ptr.reset();
b_ptr.reset();
b_ptr = that.b_ptr;
c_ptr = that.c_ptr;
return *this;
}
Note that a wrong implementation would be to simply use the default assignment operator for ResourceManager. This will assign the field in-order which implies in-order destruction of the unique_ptrs, whereas we require reverse-order destruction.
Questions
Is this usage of this pointer with placement new and the explicit destructor call safe?
Must I use the returned new_this pointer as opposed to the original this pointer (for example, if the this pointer technically becomes invalidated after calling the destructor)?
Are there any better suggested ways to achieve this? If add more such unique_ptr fields to the class, I would have to make sure that I add a copy to the assignment operator. For instance, is it possible to call the move constructor instead, like so:
ResourceManager& operator=(ResourceManager&& that) {
// Call destructor
~ResourceManager();
// Move-construct a new instance on top
ResourceManager* new_this = new(this) ResourceManager(that);
return *new_this;
}
Your solution seems overly complex.
I would code it like this:
class ResourceManager {
ResourceManager() {}
ResourceManager(A* a_ptr) :
b_ptr(new B(a)),
c_ptr(new C(b_ptr.get())) {}
ResourceManager& operator=(ResourceManager&& that)
{
// the order of these moves/assignments is important
// The old value of *(this->c_ptr) will be destroyed before
// the old value of *(this->b_ptr) which is good because *c_ptr presumably
// has an unprotected pointer to *b_ptr.
c_ptr = std::move(that.c_ptr);
b_ptr = std::move(that.b_ptr);
// (a better solution might be to use shared_ptr<B> rather than unique_ptr<B>
return *this;
}
unique_ptr<B> b_ptr;
unique_ptr<C> c_ptr;
};
Note: When the move assignment returns, that will "empty" meaning both that.b_ptr and that.c_ptr are nullptr. This is the expected result of a move assignment.
Or if "reconstructing" the target of the assignment is important (assuming there's extra code not shown in this example that makes it so) I might add a move constructor and a swap method like so:
ResourceManager(ResourceManager&& that)
: b_ptr(std::move(that.b_ptr)),
c_ptr(std::move(that.c_ptr))
{
}
void swap(ResourceManager & that)
{
b_ptr.swap(that.b_ptr);
c_ptr.swap(that.c_ptr);
}
ResourceManager& operator=(ResourceManager&& that)
{
ResourceManager temp(std::move(that));
this->swap(temp);
return *this;
}
I have following class
class TVData
{
private:
int ID;
Monitor& monitor;
string pName;
}
I need to implement the assignment operator, and a copy-constructor usable with this class.
How do I handle reference members, in this case TVData::monitor, in such scenario?
You can't reassign a reference, so if you need it to change in the assignment operator then you should make it a pointer - assignment can then be done as usual with =, although it's still encouraged to use an initialiser list in the copy constructor...
TVData(const TVData& rhs)
: ID(rhs.ID), p_monitor(rhs.p_monitor), pNmae(rhs.pName)
{ }
INTRODUCTION
To correctly assign a data member variabled declares as being a reference you will need to make use of a member initialization list in your constructor (both in your default-, and your copy-constructor).
The problem with an overload assignment operator is that since a reference cannot be bound to a new entity after it has been initialized (and it must be initialized) you cannot change what this reference is referring to after you have created your TVData.
If you'd want to be able to change what the reference refers to consider using pointers instead of references.
SAMPLE IMPLEMENTATION
#include <iostream>
struct Obj {
Obj (int& r)
: ref (r)
{ }
Obj (Obj const& src)
: ref (src.ref)
{ }
Obj& operator= (Obj const& src) {
// we cannot reassign what `ref` is refering to,
// but we can at least assign the value of `src.ref`
// to `this->ref`
ref = src.ref; // note: does not make `ref` refer to `src.ref`
}
int& ref;
};
int
main (int argc, char *argv[])
{
int some_val = 0, some_other_val = 100;
Obj a (some_val);
Obj b (a); // copy `a`
Obj c (some_other_val);
b = c;
b.ref += 23;
std::cout << "some_val: " << some_val << std::endl;
}
some_val: 123
reference are not copiable nor movable, you have to fallback to pointer or reference_wrapper
First of all, using (non-const) references as class members is fairly dangerous because in that case you have to guarantee somehow that the referenced object will outlive the class's instance referencing it.
Secondly, you can implement the copy constructor for such a class but you can't make assignment because C++ references are not rebindable.
In order to manage this, you might prefer using shared_ptr/unique_ptr depending on the assignment semantics of your class (whether it should share the owned Monitor or pass the ownership). If you're not owning it, then weak_ptr (std:: or boost::) is your best bet.
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)