c++ : move assignement operator and inheritance - c++

This code compiles and runs fine:
#include <iostream>
class Base
{
public:
Base(int value)
: clean_(true)
{
value_ = new int;
*value_ = value;
}
~Base()
{
if(clean_)
delete value_;
}
Base(Base&& other) noexcept
: value_{std::move(other.value_)},
clean_(true)
{
other.clean_=false;
}
Base& operator=(Base&& other) noexcept
{
value_ = std::move(other.value_);
other.clean_=false;
clean_=true;
}
void print()
{
std::cout << value_ << " : " << *value_ << std::endl;
}
int* value_;
bool clean_;
};
class A : public Base
{
public:
A(int v1, double v2) : Base(v1)
{
a_ = new double;
*a_ = v2;
}
A(A&& other) noexcept
: Base(std::forward<Base>(other)),
a_(std::move(other.a_))
{}
A& operator=(A&& other) noexcept
{
// should not the move assignment operator
// of Base be called instead ?
// If so: how ?
this->value_ = std::move(other.value_);
other.clean_=false;
this->clean_=true;
a_ = std::move(other.a_);
}
void print()
{
std::cout << this->value_ << " "
<< *(this->value_) << " "
<< a_ << " " << *a_ << std::endl;
}
double* a_;
bool clean_;
};
A create_a(int v1,double v2)
{
A a(v1,v2);
return a;
}
int main()
{
Base b1(20);
b1.print();
Base b2 = std::move(b1);
b2.print();
A a1(10,50.2);
a1.print();
A a2 = std::move(a1);
a2.print();
A a3 = create_a(1,2);
a3.print();
}
A is a subclass of Base.
The code of the move assignment operator of A replicates the one of Base.
Is there a way to avoid this replication of code ?

Change int* value_; to int value_; and double* a_; to double a_; and you no longer need to write any of the special member functions as the compiler provided defaults Just Work™
If you really need dynamic memory allocation, then use a RAII type like std::vector, std::unique_ptr, std::shared_ptr, ect. in its place since they are designed to be copied and or moved correctly.

Related

why child class can convert lvalue to rvalue? in c++ perfect forwarding

using namespace std;
class Data {
public:
Data() {}
~Data() {}
Data(const Data& t) { cout << "Copy Constructor" << endl; }
Data(Data&& t) noexcept { cout << "Move Constructor" << endl; }
Data& operator=(const Data& t) {
cout << "Copy =" << endl;
return *this;
}
Data& operator=(Data&& t) noexcept {
cout << "Move =" << endl;
return *this;
}
};
class Data2 : public Data {};
class Test {
public:
void setData(std::shared_ptr<Data>&& d) { data = std::forward<std::shared_ptr<Data>>(d); }
private:
std::shared_ptr<Data> data;
};
int main() {
Test t;
auto d = std::make_shared<Data>();
t.setData(d); // error
t.setData(move(d)); // ok
auto d1 = std::make_shared<Data2>();
t.setData(d1); // why ok????????????
int debug = 0;
}
Why doesn't t.setData(d1) require std::move to convert d1 to an rvalue but t.setData(d) doesn't compile?
t.setData(d1) has to convert d1 to std::shared_ptr<Data>, it does this by copying into a temporary value. Temporary values are already rvalues so don't require an explicit std::move.
Note that as d1 is passed via a temporary conversion setData doesn't alter d1, you still need to use std::move for this to happen so that d1 is moved into the temporary rather than being copied. E.g in:
auto d = std::make_shared<Data>();
t.setData(std::move(d));
std::cout << d.get() << "\n";
auto d1 = std::make_shared<Data2>();
t.setData(d1);
std::cout << d1.get() << "\n";
auto d2 = std::make_shared<Data2>();
t.setData(std::move(d2));
std::cout << d2.get() << "\n";
d and d2 will end up as null pointers but d1 will still point to the object.
Note that in std::forward<std::shared_ptr<Data>>(d) as you know the exact type of d std::forward is unnecessary and you could just use std::move(d) instead.

copy constructor, operator= in a child class

I want to make an operator= and copy constructor, to be called in the inherited class.
For normal objects, it works fine, but when I'm trying to call, for example, operator= with a pointer, it is just copying the object address.
So my question is, how can I call those methods with pointers?
#include <iostream>
// base class
class a {
public:
//constructors
a(): x(0), y(1), z(0){ std::cout << "no parameter constructor A\n"; }
a(int a, int b, int c) :x(a), y(b), z(c){ std::cout << "parameter constructor A\n"; }
a(const a& ob):x(ob.x), y(ob.y), z(ob.z)
{
std::cout << "copy constructor A\n";
}
//operator
a& operator=(const a& obj)
{
if (this != &obj)
{
x = obj.x;
y = obj.y;
z = obj.z;
}
std::cout << "operator = A\n";
return *this;
}
protected:
int x, y, z;
};
//child class
class b : public a
{
public:
//constructors
b() : p(0){ std::cout << "no parameter constructor B\n"; }
b(int X, int Y, int Z, int B) : a(X, Y, Z), p(B) { std::cout << "parameter constructor B\n"; }
b(const b& obj) :p(obj.p), a(obj)
{
std::cout << "copy constructor B\n";
}
//operator =
b& operator=(const b &obj)
{
if (this != &obj)
{
p = obj.p;
&a::operator=(obj);
}
std::cout << "operator = B\n";
return *this;
}
private:
int p;
};
int main()
{
b obj0(4, 8, 16, 32);
b obj1(obj0); // copy constructor
b obj2;
obj2 = obj1; // operator =
std::cout << std::endl << std::endl;
std::cout << "for pointers:\n\n";
a* obj3 = new b(4, 8, 16, 32);
a* obj4(obj3);
obj4 = obj3;
return 0;
}
One of the purposes of using pointers (or references) is to avoid needing to create a copy of the object. Passing a pointer to the object allows the receiver to refer to and manipulate on the original object.
If you wish the pointer to receive a new object, then you would use new.
When dealing with polymorphism as in your example, you would probably need a virtual method that creates a proper clone (sometimes called a deep copy).
class a {
//...
virtual a * clone () const = 0;
};
class b : public a {
//...
b * clone () const {
return new b(*this);
}
};
//...
a *obj4 = obj3->clone();
//...
We leverage that b * is a covariant return type for a *, so that b::clone() can return a b *, but a::clone() can use the b::clone() as an override and still return an a *.

doing deep copy vector of pointers in copy constructor got both vector member changed?

i need deep copy in my project and for now i memcpy the srcObj into destObj then
if destObj owns pointer members, i just create all the obj and do this method recursively
here's the pseudo:
class B
{
public:
B(int id_) : id(id_) {};
int id = 0;
};
class A
{
public:
vector<B*> vecInt;
B objB = 111;
A()
{
vecInt.push_back(new B(1));
vecInt.push_back(new B(2));
vecInt.push_back(new B(3));
}
A(const A& rhs)
{
memcpy(this, &rhs, sizeof(A));
for (auto i = 0; i < rhs.vecInt.size(); i++)
{
auto ptrTmp = new B(rhs.vecInt[i]->id);
cout << "00000000000 " << rhs.vecInt[i] << endl;;
this->vecInt[i] = ptrTmp;
cout << "11111111111 " << ptrTmp << endl;;
cout << "22222222222 " << rhs.vecInt[i] << endl;;
}
}
};
here's the issue, every time i assign this->vecInt[i] within the loop, the rhs.vecInt[i] changes too and they both indicate to one address, i have no idea why this happened.
appreciate any help.
The memcpy() is absolutely wrong and needs to be removed. It is corrupting your A object’s data members. It may work for the objB member, but definitely not the vecInt member.
But, even with thae memcpy() removed, you would still have undefined behavior as you are trying to assign to vector elements that don’t exist yet. To deep-copy a vector of pointers, you have no choice but to clone each dynamic B object one at a time and add it to the new vector.
The correct way to implement your copy constructor should look more like this instead:
A(const A& rhs) : objB(rhs.objB)
{
vecInt.reserve(rhs.vecInt.size());
for (auto *elem : rhs.vecInt)
{
vecInt.push_back(new B(*elem));
}
}
You also need to add a destructor, move constructor, copy assignment operator, and move assignment operator, per the Rule of 3/5/0:
class A
{
public:
vector<B*> vecInt;
B objB = 111;
A()
{
vecInt.push_back(new B(1));
vecInt.push_back(new B(2));
vecInt.push_back(new B(3));
}
A(const A& rhs) : objB(rhs.objB)
{
vecInt.reserve(rhs.vecInt.size());
for (auto *elem : rhs.vecInt)
{
vecInt.push_back(new B(*elem));
}
}
A(A&& rhs) : vecInt(move(rhs.vecInt)), objB(move(rhs.objB)) {}
~A()
{
for(auto *elem : vecInt)
delete elem;
}
A& operator=(A rhs)
{
vecInt.swap(rhs.vecInt);
objB.id = rhs.objB.id;
return *this;
}
};
That being said, consider using std::vector<std::unique_ptr<B>> instead of std::vector<B*>. That will eliminate the need for an explicit destructor. Don’t use new/delete in modern C++ if you can avoid it.
class A
{
public:
vector<unique_ptr<B>> vecInt;
B objB = 111;
A()
{
vecInt.push_back(make_unique<B>(1));
vecInt.push_back(make_unique<B>(2));
vecInt.push_back(make_unique<B>(3));
}
A(const A& rhs) : objB(rhs.objB)
{
vecInt.reserve(rhs.vecInt.size());
for (auto &elem : rhs.vecInt)
{
vecInt.push_back(make_unique<B>(*elem));
}
}
A(A&& rhs) : vecInt(move(rhs.vecInt)), objB(move(rhs.objB)) {}
~A() = default;
A& operator=(A rhs)
{
vecInt.swap(rhs.vecInt);
objB.id = rhs.objB.id;
return *this;
}
};
Even better, just use std::vector<B> instead, and let the compiler handle everything else for you:
class A
{
public:
vector<B> vecInt;
B objB = 111;
A()
{
vecInt.emplace_back(1);
vecInt.emplace_back(2);
vecInt.emplace_back(3);
}
};

How to return an object of a class which is derived from an abstract class?

So, I have a generic operator. In this specific case its generic template T is supposed to be a pointer to an abstract base class. The point is, when I create T *t and try to return *t I get an error saying T can't be instantiated because it's an abstract class. Even though *t will always be an object of derived classes which aren't abstract. It doesn't accept it, because T is after all abstract type. Basically, what I want to achieve is to get a reference to the *t returned from the operator to main() and then assign to it objects of derived classes in main().
I understand that there are questions about slicing and polymorphism. But I am trying to understand how can I achieve it in different ways. It's a completely different question which deals with the following specific code.
Code is something like this:
Template <class T>
class myArr{
T & operator [] (int index){
T*t;
Arr[0] = *t;
Return arr[0[;
}}
Main(){
Base * b=new derived;
myArr<Base>[17]=*b;
// Point is, I can make it of type pointers and maybe it can
//work but I prefer assigning *b to myArr because I want
to //assign objects themselves
}
My question is, how can I return the object in itself from the operator? I want to return something to which I can assign a derived object . I am trying to do it with pointer of type abstract base, so as to achieve polymorphism so it can contain any object of derived classes.
You cannot return an object of an abstract class. You need to return a reference or a pointer, preferably a smart one.
You cannot store an object of an abstract class. You need to store a reference or a pointer, preferably a smart one.
You cannot pass an object of an abstract class to a function as a parameter. You need to pass a reference or a pointer, preferably a smart one.
See the pattern?
Objects of abstract classes do not exist on their own. They only exist as subobjects of derived class objects, and can only be accessed via references or pointers obtained from pointers to said derived class objects.
There is no way around it. You cannot do it differently. Use smart pointers. They are the right tool for tthe job.
I want to return something to which I can assign a derived object .
This is kind of possible, depending on what exactly you want, but way more complicated than necessary. Assignment and polymorphic hierarchies don't mix well. I would not recommend that, especially if you are not sure what exactly you want. Assign, store, and pass around smart pointers.
I tried making something out of the code you showed by using std::unique_ptr<Base> in your array. It shows how you can replace objects in the array (base_arr[0] = ...) and how to update existing objects in the array (*base_arr[0] = ...). I added two derived classes carrying different types (a std::string and an int) and a lot of debug prints so it's easier to follow what's happening when you run it.
#include <iostream>
#include <vector>
#include <memory>
#include <string>
// Your container wrapper with some functions
template<class T>
class myArr {
public:
using type = T;
template<class... Args>
decltype(auto) emplace_back(Args&&... args) {
return arr.emplace_back(std::forward<Args>(args)...);
}
T& operator[](std::size_t index) { return arr[index]; }
auto begin() const { return arr.begin(); }
auto end() const { return arr.end(); }
T extract_front() {
T retval(std::move(arr.front()));
arr.erase(arr.begin());
return retval;
}
private:
std::vector<T> arr{};
};
//----------------------------------------------------------------------------------
struct Base {
Base() = default;
Base(const Base&) = default;
Base(Base&&) = default;
virtual ~Base() = 0;
virtual Base& operator=(const Base&) = 0;
virtual Base& operator=(Base&&) = 0;
virtual void print() = 0;
};
Base::~Base() {}
Base& Base::operator=(const Base&) {
std::cout << "Base& Base::operator=(const Base&)\n"; // nothing real to copy here
return *this;
}
Base& Base::operator=(Base&&) {
std::cout << "Base& Base::operator=(Base&&)\n"; // nothing real to move here
return *this;
}
//----------------------------------------------------------------------------------
struct der_1 : public Base {
der_1(const std::string& value) : Base(), m_value(value) {
std::cout << "der_1(" << m_value << ") converting\n";
}
der_1(const der_1& rhs) : Base(rhs), m_value(rhs.m_value) {
std::cout << "der_1(" << m_value << ") copy\n";
}
der_1(der_1&& rhs) : Base(std::move(rhs)), m_value(std::move(rhs.m_value)) {
std::cout << "der_1(" << m_value << ") move\n";
}
~der_1() { std::cout << "~der_1(" << m_value << ")\n"; }
der_1& operator=(const der_1& rhs) {
std::cout << "der_1& der_1::operator=(const der_1&)\n";
if(this == &rhs) return *this; // no self-assignment
Base::operator=(rhs); // copy the Base part of rhs
m_value = rhs.m_value; // copy the der_1 specific part
return *this;
}
der_1& operator=(der_1&& rhs) {
std::cout << "der_1& der_1::operator=(der_1&&)\n";
Base::operator=(std::move(rhs)); // move the Base part of rhs
m_value = std::move(rhs.m_value); // move the der_1 specific part
return *this;
}
// override Base's copy assignment
Base& operator=(const Base& rhs) override {
std::cout << "Base& der_1::operator=(const Base&)\n";
// downcasting may throw bad_cast
const der_1& rhsref = dynamic_cast<const der_1&>(rhs);
return *this = rhsref; // call standard copy assignment
}
// override Base's move assignment
Base& operator=(Base&& rhs) override {
std::cout << "Base& der_1::operator=(Base&&)\n";
// downcasting may throw bad_cast
der_1& rhsref = dynamic_cast<der_1&>(rhs);
return *this = std::move(rhsref); // call standard move assignment
}
void print() override { std::cout << "der_1::print(" << m_value << ")\n"; }
private:
std::string m_value;
};
//----------------------------------------------------------------------------------
struct der_2 : public Base {
der_2(int value) : Base(), m_value(value) {
std::cout << "der_2(" << m_value << ") converting\n";
}
der_2(const der_2& rhs) : Base(rhs), m_value(rhs.m_value) {
std::cout << "der_2(" << m_value << ") copy\n";
}
der_2(der_2&& rhs) : Base(std::move(rhs)), m_value(std::move(rhs.m_value)) {
std::cout << "der_2(" << m_value << ") move\n";
}
~der_2() { std::cout << "~der_2(" << m_value << ")\n"; }
der_2& operator=(const der_2& rhs) {
std::cout << "der_2& der_2::operator=(const der_2&)\n";
if(this == &rhs) return *this; // no self-assignment
Base::operator=(rhs); // copy the Base part of rhs
m_value = rhs.m_value; // copy the der_2 specific part
return *this;
}
der_2& operator=(der_2&& rhs) {
std::cout << "der_2& der_2::operator=(der_2&&)\n";
Base::operator=(std::move(rhs)); // move the Base part of rhs
m_value = std::move(rhs.m_value); // move the der_2 specific part
return *this;
}
// override Base's copy assignment
Base& operator=(const Base& rhs) override {
std::cout << "Base& der_2::operator=(const Base&)\n";
// downcasting may throw bad_cast
const der_2& rhsref = dynamic_cast<const der_2&>(rhs);
return *this = rhsref; // call standard copy assignment
}
// override Base's move assignment
Base& operator=(Base&& rhs) override {
std::cout << "Base& der_2::operator=(Base&&)\n";
// downcasting may throw bad_cast
der_2& rhsref = dynamic_cast<der_2&>(rhs);
return *this = std::move(rhsref); // call standard move assignment
}
void print() override { std::cout << "der_2::print(" << m_value << ")\n"; }
private:
int m_value;
};
//----------------------------------------------------------------------------------
int main() {
myArr<std::unique_ptr<Base>> base_arr;
{
{
std::cout << "-- put pointers to objects of derived classes in base_arr --\n";
base_arr.emplace_back(std::make_unique<der_1>("howdy"));
base_arr.emplace_back(std::make_unique<der_2>(10));
std::cout << "\n-- print what we've got --\n";
for(auto& b : base_arr) b->print();
std::cout << "\n-- set new value for an existing object, by copying --\n";
der_1 something_to_copy("something_to_copy");
*base_arr[0] = something_to_copy;
std::cout << "\n-- set new value for an existing object, by moving --\n";
*base_arr[0] = der_1("something_to_move");
std::cout << "\n-- try to assign a der_2 to a der_1 --\n";
try {
*base_arr[0] = der_2(666);
} catch(const std::exception& ex) {
std::cout << "Exception: " << ex.what() << "\n";
}
std::cout << "\n-- replace a der_1 object with a der_2 object --\n";
base_arr[0] = std::make_unique<der_2>(20);
std::cout << "\n-- destroying something_to_copy since it goes out of "
"scope --\n";
}
std::cout << "\n-- stuff in base_arr --\n";
for(auto& b : base_arr) b->print();
std::cout << "\n-- extract front, got:\n";
auto ptr = base_arr.extract_front();
ptr->print();
std::cout << "\n-- the above dies, goes out of scope --\n";
}
std::cout << "\n-- base_arr is about to be destroyed --\n";
}

Applying the Rule of Five with Inheritance and Virtual Functions

Every example that I've seen which implement the Rule of Five, implements the rule on a class without inheritance and polymorphism (virtual functions).
How can the rule of 5 be applied to the sample code bellow?
The test example uses c-arrays for dynamic objects. This is slightly contrived of course in order to have dynamic objects to manage to demonstrate the Rule of Five. In practice, it could have been anything (FILE ptrs, database handles, etc). Therefore, don't suggest using vector, or turning the exercise into demonstration of the Rule of Zero.
I'd prefer the solution not to use the copy-swap idiom because I find the non-copy-swap method more explicit in the case of non-inheriting classes. But if one wished to illustrate both methods, that would also be great.
Notes about sample code:
B inherits from A.
Both A & B have their own dynamic object to manage (a contrived c-array).
Class T is a contrived class to have custom type for diagnostic prints. The c-arrays store T elements.
Class A & B are void of Rule of Five implementation (to be defined by answer)
The c-arrays have not yet been allocated in the sample code, but of course, they would be at construction based on respective bufferSize.
Sample Code
//TEST TYPE
class T
{
public:
T() { cout << "ctorT" << endl; }
~T() { cout << "dtorT" < endl; }
T(const T& src) { cout << "copy-ctorT " << endl; }
T& operator=(const T& rhs) { cout << "copy-assignT" << endl; return *this; }
T(T&& src) noexcept { cout << "move-ctorT " << endl; }
T& operator=(T&& rhs) { cout << "move-assignT" << endl; return *this; }
}
class A
{
string nameParent = "A";
size_t bufferSizeParent = 0;
T *pBufferParent = nullptr; //c-array
public:
A(string tNameParent, size_t tBufferSizeParent) : nameParent(tNameParent), bufferSizeParent(tBufferSizeParent)
{
cout << "ctorA " << nameParent << endl;
}
virtual ~A(){ cout << "dtorA " << nameParent << endl; }
virtual string getName() { return nameParent; }
virtual void setName(const string name) { nameParent = name; }
};
class B : public A
{
string nameChild = "B";
size_t bufferSizeChild = 0;
T *pBufferChild = nullptr; //c-array
public:
B(string tNameChild, string tNameParent, size_t tBufferSizeChild, size_t tBufferSizeParent)
: A(tNameParent, tBufferSizeParent), nameChild(tNameChild), bufferSizeChild(tBufferSizeChild)
{
cout << "ctorB " << nameChild << endl;
}
~B(){ cout << "dtorB " << nameChild << endl; }
virtual string getName() override final { return nameChild; }
virtual void setName(const string name) override final { nameChild = name; }
};
A good rule of thumb is that a class should directly manage at most one resource. Other classes have all defaulted special members, preferably implicitly, but explicitly if they are to be declared in some variation.
Your example becomes
struct T;
extern T acquire_T();
extern void release_T(T);
class THandle
{
T handle;
public:
THandle()
: handle(acquire_T()) {}
~THandle() { release_T(handle); }
THandle(const THandle &) = delete;
THandle(THandle && other)
: handle(other.handle)
{ other.handle = {}; }
THandle & operator=(const THandle &) = delete;
THandle & operator=(THandle && other)
{ std::swap(handle, other.handle); }
}
class A
{
std::string nameParent = "A";
std::size_t bufferSizeParent = 0;
THandle tParent;
public:
A() {}
A(std::string tNameParent, std::size_t tBufferSizeParent) : nameParent(tNameParent), bufferSizeParent(tBufferSizeParent)
{ }
virtual ~A() = default;
A(const A &) = default;
A(A &&) = default;
A & operator=(const A &) = default;
A & operator=(A &&) = default;
virtual string getName() { return nameParent; }
virtual void setName(const string name) { nameParent = name; }
};
class B : public A
{
std::string nameChild = "B";
std::size_t bufferSizeChild = 0;
THandle tChild;
public:
B() {}
B(string tNameChild, string tNameParent, size_t tBufferSizeChild, size_t tBufferSizeParent)
: A(tNameParent, tBufferSizeParent), nameChild(tNameChild), bufferSizeChild(tBufferSizeChild), bufferChild(std::make_unique(tBufferSizeChild))
{ }
virtual string getName() override final { return nameChild; }
virtual void setName(const string name) override final { nameChild = name; }
};