If I use a const reference to another member,
is it possible that this reference gets invalidated?
class Class {
public:
const int &x{y};
private:
int y;
};
For example when I use instances of this class in a vector
which increases its capacity after a push_back.
According to the standard all iterators and references are invalidated if
a vector has to increase its capacity. Is the reference still valid after that?
This is currently not safe, as when you copy an instance of Class, x will reference the y of the copied object, not its own y. You can see this by running the following code:
int main()
{
Class a{};
std::vector<Class> vec;
vec.push_back(a);
//these lines print the same address
std::cout << &(a.x) << std::endl;
std::cout << &(vec[0].x) << std::endl;
}
You can fix this by writing your own copy constructor and assignment functions to correctly initialize x:
Class (const Class& rhs) : x{y}, y{rhs.y} {}
This is safe, becausex and y will only be destroyed along with your object. Invalidation of references for std::vector means references to the vector elements:
Class c;
std::vector<Class> vec;
vec.push_back(c);
Class& cr = vec[0];
//other operations on vec
std::cout << c.x; //fine, that reference is internal to the class
std::cout << cr.x; //cr could have been invalidated
Assuming this is a reference to another member from the same instance, you need to override copy constructor to initialize it. Default copy constructor will copy the reference to 'y' from the old instance which might be invalidated.
Why do you need a reference to a member is another question.
P.S. also you need to override an assignment operator for the same reason.
Related
In the following code, I want to disable the move construction of base class Vector from derived class VectorMap, and call the copy constructor.
#include <iostream>
#include<algorithm>
struct Vector{
int* _ptr=nullptr;
int _size=0;
Vector(int n){
_ptr = new int[n];
_size = n;
std::cout<< " Construct "<<this<<std::endl;
}
Vector(void) { std::cout <<" Construct " << this << std::endl; }
virtual ~Vector(void) {
if (_ptr != nullptr) {
std::cout << "Deconstruct " << this << " -> delete " << _ptr << std::endl;
delete _ptr;
return;
}
std::cout << "Deconstruct " << this << std::endl;
}
Vector(Vector&& v2) noexcept {
int* p2=v2._ptr; int s2=v2._size;
v2._ptr=_ptr;
v2._size=_size;
_ptr=p2; _size=s2;
std::cout << "Move construct " << this << std::endl;
}
Vector(const Vector& v3){
_ptr=new int[v3._size];
_size=v3._size;
memcpy(_ptr,v3._ptr,sizeof(int) * _size);
}
};
struct VectorMap
: public Vector {
VectorMap(int* p,int size){
_ptr=p;
_size=size;
}
~VectorMap(void) override {
_ptr=nullptr; _size=0;
}
};
int main(void) {
Vector v1(10);
Vector v2=VectorMap(v1._ptr,5); // v1._ptr will be deleted twice
return sizeof(v2);
}
As you can see, if move constructor is called in the line Vector v2=VectorMap(v1._ptr,5);, the data pointer in v1 will be deleted twice, one is by v2, and another is by v1. Since they share the same pointer. Is there any way to modify VectorMap to call copy constructor rather than move constructor in such case?
The primary problem is muddy ownership semantics.
Vector creates a resource (dynamic array), and is apparently intended to have unique ownership over it.
VectorMap on the other hand takes a pointer in its constructor, and gives it to its base Vector that takes ownership. But right before destruction, VectorMap rescinds the ownership by setting the base Vector pointer to null. So, VectorMap sort of pretends to own the resource until it's time for the responsibility of the ownership at which point it backs down.
This inheritance also causes other problem situations. Consider for example making a copy of VectorMap. Look at what the copy constructor of the base does. It allocates memory for the copy. But the desturctor of the VectorMap copy sets the pointer to null, so in this case the pointer is deleted zero times. It leaks memory.
As an analogy, disabling slicing would be a bandage for the wound, but what you really should do is to not stick your hand inside a running blender. If VectorMap is supposed to not have ownership, then it shouldn't inherit a base that takes ownership. It's unclear to me what the point of VectorMap class even is.
Furthermore, a class that has unique ownership of a resource such as Vector really should encapsulate that bare pointer with private access specifier. Sure, such classes sometimes still provide a way to copy or throw away that value (like std::vector::data and std::unique_ptr::release), but it's important that only happens through a specific function to reduce chance of accidental violation of ownership semantics.
Another serious bug:
_ptr=new int[v3._size];
// ...
delete _ptr;
You must not delete a pointer to a dynamic array. You must use delete[]. Using delete results in undefined behaviour.
Another bug: You forgot to include the header that declares memcpy. Also, I would recommend using std::copy instead.
This simple code:
#include <iostream>
#include <vector>
struct my_struct
{
int m_a;
my_struct(int a) : m_a(a) { std::cout << "normal const " << m_a << std::endl; }
my_struct(const my_struct&& other) : m_a(other.m_a) { std::cout << "copy move " << other.m_a << std::endl; }
my_struct(const my_struct &other) : m_a(other.m_a) { std::cout << "copy const " << other.m_a << std::endl; }
};
class my_class
{
public:
my_class() {}
void append(my_struct &&m) { m_vec.push_back(m); }
private:
std::vector<my_struct> m_vec;
};
int main()
{
my_class m;
m.append(my_struct(5));
m.append(std::move(my_struct(6)));
}
produces this output:
normal const 5
copy const 5
normal const 6
copy const 6
copy const 5
The first call to append creates the object, and push_back creates a copy. Likewise, the second call to append creates the object, and push_back creates a copy. Now, a copy constructor of the first object is mysteriously called. Could someone explain me what happens? It looks like a strange side effect...
Now, a copy constructor of the first object is mysteriously called. Could someone explain me what happens? It looks like a strange side effect...
When you call push_back on std::vector, vector may need to grow it's size as stated in the cppreference:
If the new size() is greater than capacity() then all iterators and references (including the past-the-end iterator) are invalidated. Otherwise only the past-the-end iterator is invalidated.
You can use reserve before pushing anything to your vector. Try this:
class my_class
{
public:
my_class()
{
m_vec.reserve(10); // Use any number that you want.
}
void append(my_struct &&m) { m_vec.push_back(m); }
private:
std::vector<my_struct> m_vec;
};
Few other issues with your program:
You need to fix signature of your move constructor as move constructor requires rvalue reference (more specifically, xvalue or prvalue). It should like this:
my_struct(my_struct&& other) noexcept : m_a(other.m_a)
{
std::cout << "copy move " << other.m_a << std::endl;
}
noexcept is required as we need to inform C++ (specifically std::vector) that move constructor and destructor does not throw, using noexcept. Then the move constructor will be called when the vector grows. See this.
The method append should be:
void append(my_struct &&m)
{
m_vec.push_back(std::move(m));
}
To know why we need to use std::move on rvalue reference, see this Is an Rvalue Reference an Rvalue?. It says:
Things that are declared as rvalue reference can be lvalues or rvalues. The distinguishing criterion is: if it has a name, then it is an lvalue. Otherwise, it is an rvalue.
If you don't use std::move, then copy constructor would be called.
That's just how std::vector works!
When you call push_back(), the underlying array needs to grow to make room for the new element.
So internally, a new larger array is allocated and all the elements of the previous smaller array are copied into the freshly created array. This also comes with some overhead. Now, you can use some techniques to optimize away the copies.
If you have an idea of how large the array could grow, you can use the reserve() method to ensure that no resizing will occur upto that many locations.
vct.reserve(5)
This is will ensure that no resizing will occur until 5 elements.
Also, you can use the emplace_back() function to avoid an additional copy. It constructs the object in place. Simply pass the constructor parameters of the object to emplace_back()
#include <iostream>
#include <vector>
class myClass {
public:
double a;
double& ref;
myClass() : ref(a)
{
a = 1;
}
~myClass() {}
};
using namespace std;
int main()
{
vector<myClass> myVector;
int nIter = 5;
while (nIter--) {
myVector.push_back(myClass());
}
return 0;
}
Hi.
I have myClass and I would like to push_back the myClasses and bring them together in one vector.
But unfortunately, I have to use a reference in myClass.
The problem is when the temporary object destructs, the reference becomes invalid and vector contains the object whose reference is invalidated.
After investigating, I was able to see that those reference variables are pointing at (referencing) the same memory.
I would like to find the way where each vector's element's reference member variable references each vector's element's a(member variable).
Is there any way to achieve this..?
Addition
I would like to describe my situation further.
I have one middle-sized project. In there, the users have the option to choose which variable among member variables will be used in my algorithm. so I made the script in which ref variable is used so that it can change according to the option.
I hope my explanation was clear.
You're getting this behavior because you are initializing your 'ref' member to a value that is on the stack, then the default copy constructor is copying that into the vector.
For example, in my debugger the value I have for ref is:
+ &myVector[1].ref 0x00eff80c {2.0000000000000000} double *
+ &myVector[1] 0x00126940 {a=2.0000000000000000 ref=2.0000000000000000 }
+ myVector { size=0x00000002 } std::vector<myClass,std::allocator<myClass> >
+ &nIter 0x00eff8f0 {0xffffffff} int *
You can see that myVector[1].ref is not inside myVector[1] as you'd expect, and is, in fact, on the stack. You can see that nIter and ref are only 57 bytes apart:
&nIter - (int*)&myVector[0].ref 57
If you want to see how this is implicitly happening you can delete your copy constructor:
myClass(myClass const &rhs) = delete;
inside myClass and you'll get an error at push_back.
Another option is to write your own copy constructor:
myClass(myClass const &rhs) : ref(a) {
a = rhs.a;
}
If you debug this you'll see that the values are correct, and that the memory locations of each ref are now inside the bounds of the myClass objects.
Finally you might be able to use emplace_back instead of push_back, which will construct myClass directly in the vector's memory instead of calling the copy ctor, though I wouldn't recommend this as it leaves this ref copy bug.
also don't forget the assignment operator if you go the copy ctor route:
https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)
e.g.
myClass baz;
baz = myVector[0];
this will invoke operator= and not the copy ctor. My compiler (visual studio c++ latest) automatically deletes operator= if you declare a copy ctor, so it would catch this, but your compiler may not.
I have a class that holds a std::vector like
struct Mystruct
{
Mystruct(const std::vector<int>& w): v(w)
{
std::cout << "Copy constructor :" << v.at(0) << "\n";
}
Mystruct(const std::vector<int>&& w): v(w)
{
std::cout << "Move Constructor :" << v.at(0) << "\n";
}
private:
std::vector<int> v;
};
And I create objects like
int main()
{
auto x = std::vector<int> {1,2,3};
Mystruct M1(x);
Mystruct M2(std::vector<int> {3,2,1});
return 0;
}
M1 is constructed using the copy constructor and M2 using the "move" constructor, however running in gdb both assignements keep different addresses for v and w, the same happens if I use v (std::move(w)) in the initialization list of the second constructor. so I guess that both assignement are copying the contents of w, is this correct? if its the case, how can I make to move the contents of w instead of copying them
First of all, moving an object does not change its address. You can't, by definition, change the address of an object, since an address is part of the definition of an object. If you have a different address, you have a different object. What moving does (at least in regards to classes in the standard library) is transfer ownership of the dynamically allocated resources of an object.
Second, you're not moving anything. In order to move something, you have to call the move constructor. Which means you need to pass an r-value. A named object is never an r-value. So even though you have an r-value reference, you still need to cast it with std::move. But in your case, even that's not enough. In the standard library, all move constructors have signatures of Type(Type&&), never Type(const Type&&). If you pass a const value, even if you cast it with std::move, it will invoke the copy constructor, not the move constructor. In order to invoke the move constructor, your object must not be const.
So your constructor that moves a vector* should look like this:
Mystruct(std::vector<int>&& w): v(std::move(w))
{
std::cout << "Move Constructor :" << v.at(0) << "\n";
}
And in order to compare addresses to see if it was actually moved, you should not be comparing the addresses of v and w (those will not, and cannot change), but v.data() and w.data(), which point to the dynamically allocated resources of the vector.
*not a move constructor. A move constructor would have the signature Mystruct(Mystruct&&) or Mystruct(const Mystruct&&)
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.