I'm trying to create an immutable class in C++11, which (for convenience) provides methods that modify the current state. To satisfy the immutable contract, these methods must return a new instance with the updated contents.
In the following snippet, ImmutableObject::setA() and ImmutableObject::setB() update the a and b attributes in a new instance of ImmutableObject created via a copy constructor and return it. The method print() just displays the content.
int main(int argc, char **argv) {
ImmutableObject().setA(1).setB(2).print();
return 0;
}
(An implementation of ImmutableObject is provided below.)
Implementing this naively using C++03 causes an unnecessary copies to be created.
I am wondering if C++11 move semantics can be used to exploit the fact that ImmutableObject() and the return value of setA() are just temporary copies that could be directly updated.
I already added a rvalue copy and assignment constructor in the code below, but it does not look like it's working. When running the program, I get
In constructor
In copy constructor
In copy constructor
a=1, b=2
Any advice would be greatly appreciated!
#include <iostream>
using namespace std;
class ImmutableObject {
public:
ImmutableObject() {
std::cout << "In constructor" << std::endl;
}
ImmutableObject(const ImmutableObject &obj) : a(obj.a), b(obj.b) {
std::cout << "In copy constructor" << std::endl;
}
ImmutableObject(ImmutableObject&& other) {
std::cout << "In move constructor" << std::endl;
a = other.a;
b = other.b;
}
ImmutableObject &operator=(ImmutableObject&& other) {
if (this != &other) {
a = other.a;
b = other.b;
}
return *this;
}
ImmutableObject setA(int value) {
ImmutableObject result(*this);
result.a = value;
return result;
}
ImmutableObject setB(int value) {
ImmutableObject result(*this);
result.b = value;
return result;
}
void print() {
std::cout << "a=" << a << ", b=" << b << std::endl;
}
private:
int a;
int b;
};
Related
I am starting with C++ environment, I hope I didn't mess up a lot with the concepts here.
I have one task where I need to create an object that has to be able to be copied in two ways, with a shallow copy and a deep copy.
The object has to allow two types of copies. Either the object is shallow copied or deep copy depending on the demands.
In the code below I create a simple example to explain the use case. There, I implement the object Object with a one-argument constructor and a deep copy constructor. Then at some point we use the functions get_shallow and get_deep.
Is there a way to tell the compiler which constructor to use when it copies Object in the scope of the functions? Sometimes I will need a function to return the shallow copy and other times a deep copy.
using namespace std;
class Object;
typedef std::shared_ptr<Object> objectPtr;
class Object{
private:
int *data;
public:
Object(int d);
Object(const Object &source);
~Object();
};
// One argument constructor
Object::Object(int d) {
data = new int;
*data = d;
}
//deep copy constructor
Object::Object(const Object &source) :Object(*source.data) {}
Object::~Object() {delete data;}
Object get_shallow(Object object) {
return object;
}
Object get_deep(Object object) {
return object;
}
int main(){
Object object1 {100};
get_deep(object1); //returns a deep copy of the object
get_shallow(object1); //returns a shallow copy of the object
return 0;
}
You can use a tag on the copy constructor to indicate it is making a shallow copy.
Note carefully that a shallow copy does not own the resources. So when the owner gets destructed, any shallow copy will have dangling pointers. That's a very fragile situation, and easily the source of bugs.
An alternative solution is to have a std::shared_ptr for the shared resources, and then those resources exist until all owners relinquish ownership (typically relinquishing ownership upon destruction, but could be that they change ownership over the lifecycle of the entity object).
Regardless, here's an example of shallow copy constructor.
#include <iostream>
using std::cout;
using std::ostream;
class Object {
int* data;
bool delete_data = true;
public:
enum Shallow_tag { shallow };
~Object();
Object(int d);
Object(Object const&); // DEEP copy.
Object(Object const&, Shallow_tag); // SHALLOW copy.
bool is_shallow() const { return !delete_data; }
auto get_data() const -> int* { return data; }
};
Object::~Object() { if (delete_data) delete data; }
// One argument constructor
Object::Object(int d) : data{new int(d)} { }
//deep copy constructor
Object::Object(Object const& other) : Object(*other.data) {}
// shallow copy
Object::Object(Object const& other, Shallow_tag) : data{other.data}, delete_data{false} {}
Object get_shallow(Object const& object) {
return Object(object, Object::shallow);
}
Object get_deep(Object object) {
return object;
}
ostream& operator<<(ostream& out, Object const& obj) {
out << (obj.is_shallow() ? "shallow" : "deep") << " ";
auto d = obj.get_data();
if (d) out << "data:" << *d;
else out << "data:(null)";
return out;
}
int main() {
auto object1 = Object{100};
auto obj2 = get_deep(object1); //returns a deep copy of the object
auto obj3 = get_shallow(object1); //returns a shallow copy of the object
cout << "obj2 is " << obj2 << "\n";
cout << "obj3 is " << obj3 << "\n";
}
UPDATE: using shared_ptr for the data.
Of course, an int as shared data is probably a bit silly. But for the purposes of an example it is illustrative of whatever the data payload type.
#include <iostream>
#include <memory>
using std::cout;
using std::make_shared;
using std::ostream;
using std::shared_ptr;
class Object {
shared_ptr<int> data;
public:
Object(int d);
auto get_data() const -> int* { return data.get(); }
};
Object::Object(int d) : data{make_shared<int>(d)} { }
auto operator<<(ostream& out, Object const& obj) -> ostream& {
auto d = obj.get_data();
if (d) out << "data:" << *d;
else out << "data:(null)";
return out;
}
Object get_copy(Object o) {
return o;
}
int main() {
auto object1 = Object{100};
auto obj2 = get_copy(object1);
auto obj3 = get_copy(object1);
cout << "obj2 is " << obj2 << "\n"; // 100
cout << "obj3 is " << obj3 << "\n"; // 100
*object1.get_data() = 5;
cout << "obj2 is " << obj2 << "\n"; // 5
cout << "obj3 is " << obj3 << "\n"; // 5
obj2 = Object{75};
cout << "obj2 is " << obj2 << "\n"; // 75
cout << "obj3 is " << obj3 << "\n"; // 5
}
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";
}
I am learning cpp, and now have a problem. Here are the code and the result.
What I very want to know are why there is only one constructor between "1" and "2" and why there are assignment and constructor between "3" and "4".
Thank you in advance
the result
#include <iostream>
#include <string>
using namespace std;
class A{
public:
A(){
cout << "Empty-constructor" << endl;
}
A(const A &a){
cout << "Copy-constructor" << endl;
this->v = a.v;
}
A operator=(const A &a){
cout << "Assignment" << endl;
this->v = a.v;
return *this;
}
int get(){
return v;
}
void set(int v){
this->v = v;
}
private:
int v;
};
A func(){
A a;
return a;
}
int main(){
cout << "1" << endl;
A b = func();
cout << "2" << endl;
A c;
cout << "3" << endl;
c = b;
cout << "4" << endl;
return 0;
}
A b = func(); only produces construction due to copy elision/NRVO; the new A is constructed directly into the caller's memory.
b = c; involves both assignment and construction because you wrote your assignment operator incorrectly, having it return by value, rather than by reference, so after the assignment is performed, it copy-constructs from the object you've just assigned to, returning the copy (which isn't used, and gets thrown away immediately). That's a giant waste, and should really be fixed to make the assignment operator return by reference, changing:
A operator=(const A &a){
to:
A& operator=(const A &a){
Even better, use the copy-and-swap idiom to avoid duplicating code for copying/swapping all over the place.
I have the following sample code, and the copy assignment is doing something I don't want - it's first constructing the new samp(6), then copying it to z, then destroying the new samp(6) it built. Is there a way to change the constructors such that = acts like a pointer, destroying the originally constructed samp(5) and replacing it with the new samp(6), calling the destructor on samp(5), rather than samp(6)?
#include <iostream>
class samp
{
public:
samp(int a)
{
m_a = a;
std::cout << "cons" <<m_a << std::endl;
}
int m_a;
samp(const samp& other)
{
std::cout << "copy" << m_a << std::endl;
m_a = other.m_a;
}
samp& operator= (const samp& other)
{
std::cout << "assg" << m_a << std::endl;
samp* z =new samp(other.m_a);
return *z;
}
~samp()
{
std::cout << "dest" <<m_a<< std::endl;
}
};
int main()
{
samp z(5);
z = samp(6);
std::cout << z.m_a << std::endl;
return 0;
}
Maybe pointer semantics is what you want:
#include <memory>
// ...
auto z = std::make_unique<samp>(5);
z = std::make_unique<samp>(6); // dest5
std::cout << z->m_a << '\n'; // 6
Although if you are coming to C++ from a language where object names are object references, it may be better to get used to C++ value semantics instead of trying to replicate object references :)
operator= is a member of your object so the this pointer is available. Also, assignment means the target of the assignment should change. Your version is creating a new object, but leaving the target alone. See if this does what you want:
samp& operator= (const samp& other)
{
m_a = other.m_a;
return *this;
}
I can't figure out how push_back(const value_type& val) exactly works, in docs it says about val that
val is Value to be copied (or moved) to the new element ...
How it can be copied when it takes val by reference ?
Will that copying ever call the copy constructor of val ?
and what's exactly happening here ?
#include <iostream>
#include <vector>
using namespace std;
struct x
{
x(int v = 0) : v(v) {}
int v;
};
vector<vector<x>> parts;
void fillParts()
{
vector<x> values = { x(1), x(2), x(3) };
parts.push_back(values);
}
int main()
{
fillParts();
parts[0][0].v = -123;
cout << parts[0][0].v; // -123
return 0;
}
this runs with no erros,
is parts[0] is a reference to local vector values or a copy ?
if it is a reference shouldn't it at least give some warnings saying that your accessing and modifying local objects of freed stack ?
How it can be copied when it takes val by reference?
Think of a copy constructor.
It takes parameter by reference, and it performs copying perfectly.
class Bar
{
public:
Bar(const Bar & rhs); // by reference, to copy.
};
Will that copying ever call the copy constructor of val ?
Copy operation uses copy constructor.
You can actually see if it's copied, or moved by providing user-defined constructors.
struct x
{
public:
x(const x & rhs)
{
// Some copy operation.
std::cout << "Copied" << std::endl;
}
x(x && rhs)
{
// Some move operation.
std::cout << "Moved" << std::endl;
}
};
You can try this
class A
{
public:
A() {}
A(const A&) { cout << "copy cons" << endl; }
A& operator= (A &&) { cout << "move" << endl; };
A& operator= (const A &) { cout << "copy" << endl; };
};
vector<A> parts;
void fillParts()
{
A a;
parts.push_back(a);
}
int main()
{
fillParts();
return 0;
}
I got copy cons called in both debug and release builds.