Here is my code:
class Test
{
public:
Test(){ cout << "constructor" << endl; }
~Test(){ cout << "destructor" << endl; }
void show(){ cout << "show" << endl; }
};
int main()
{
vector<shared_ptr<Test>> vec;
vec.push_back(make_shared<Test>(Test()));
vec[0]->show();
vec.clear();
return 0;
}
I run the code above in Visual Studio and here is the result:
constructor
destructor
show
destructor
In my opinion, constructor and destructor before show come from Test(). We created a temporary object as the parameter of make_shared<>(). So here we call the constructor and destructor.
But I don't know why there isn't any constructor after show. Won't make_shared new an object? How could we have a destructor without any constructor after show? Or it's because the compiler did something that I don't know?
Gotta be aware of compiler-generated copy constructors here (or elided copies depending on compiler implementation):
class Test
{
public:
Test(){ cout << "constructor" << endl; }
~Test(){ cout << "destructor" << endl; }
Test(const Test& other){cout << "copy" << std::endl;}
void show(){ cout << "show" << endl; }
};
int main()
{
vector<shared_ptr<Test>> vec;
vec.push_back(make_shared<Test>(Test()));
vec[0]->show();
vec.clear();
return 0;
}
constructor
copy
destructor
show
destructor
Demo
The generation of a copy constructor when there is a user defined destructor is deprecated, so in our case the compiler is either eliding the copy, or actually generating a copy constructor.
Edit
A word to the wise. The Rule of Three has become the Rule of Five (and a half) in C++11 and beyond. I personally like the Rule of all or nothing which essentially states that if you define any one of the special member functions (ctors, dtor, assignment), then you should define ALL of them (explicitly default or delete them, that is)
Related
I'm writing a test program about c++ type erasure, the code is put on the end.
when I run program , the test case 2 output as follow:
A default cstr...0x7ffe0fe5158f
obj_:0x7ffe0fe5158f objaaa 0x7ffe0fe5158f
Print A 0x7ffe0fe5158f
my machine: Linux x86-64, gcc 4.8
In my opinion, "Object obj2(a2);" makes a class Model by lvalue reference, so it should call A's copy constructor,
but actually it did not work, it makes me confused.
someone can give a explanation, thank you in advance.
the program is list as follow:
#include <memory>
#include <iostream>
class Object {
public:
template <typename T>
Object(T&& obj) : object_(std::make_shared<Model<T>>(std::forward<T>(obj))) {
}
void PrintName() {
object_->PrintName();
}
private:
class Concept {
public:
virtual void PrintName() = 0;
};
template <typename T>
class Model : public Concept {
public:
Model(T&& obj) : obj_(std::forward<T>(obj)) {
std::cout << "obj_:" << std::addressof(obj_) <<" objaaa " << std::addressof(obj) << std::endl;
}
void PrintName() {
obj_.PrintName();
}
private:
T obj_;
};
private:
std::shared_ptr<Concept> object_;
};
class A {
public:
A(A& a) {
std::cout<< "A copy cstr...a" << this << std::endl;
}
A(A&& a) {
std::cout << "A move cstr...." <<this<< std::endl;
}
A() {
std::cout << "A default cstr..." <<this<< std::endl;
}
void PrintName() {
std::cout << "Print A " << this << std::endl;
}
};
int main(void)
{
// test case 1
Object obj{A()};
obj.PrintName();
// test case 2
A a2;
Object obj2(a2);
obj2.PrintName();
return 0;
}
In Object obj2(a2);, no copy is made. T in the constructor of Object is deduced to be A&, so it instantiates Model<A&>, which stores a reference to the original a2 object as its obj_ member.
Observe that in your debug output, a2's constructor, Model's constructor and PrintName all print the same address. You can further confirm that this address is in fact &a2.
My concern is about run-time polymorphism. I quite understand the notion of using base-class pointers and the "virtual" keyword in order to achieve it. And I do understand why the compiler defers call resolution to run-time. But, I am confused thinking why the compiler would defer it when references are used, instead of pointers. A reference, once assigned, cannot refer to anything else. So, doesn't the compiler already know what object the reference refers to? So, why doesn't it statically resolve function calls when there is a base-class reference, not pointer? This is different for a pointer because it can point to any object at run-time within the class hierarchy. But the reference is fixed!
Here is a code snippet:
class Base
{
protected:
int m_value;
public:
Base(int value)
: m_value(value)
{
}
virtual const char* getName() const { return "Base"; }
int getValue() const { return m_value; }
};
class Derived: public Base
{
public:
Derived(int value)
: Base(value)
{
}
virtual const char* getName() const { return "Derived"; }
};
int main()
{
Derived derived(5);
std::cout << "derived is a " << derived.getName() << " and has value " <<
derived.getValue() << '\n';
Base &ref = derived;
std::cout << "ref is a " << ref.getName() << " and has value " << ref.getValue() << '\n';
Base *ptr = &derived;
std::cout << "ptr is a " << ptr->getName() << " and has value " << ptr- >getValue() << '\n';
return 0;
}
"The reference is fixed" premise is false. A reference may refer to the base subobject of any object in the hierarchy, just like the pointer can. The compiler cannot tell from the reference what the most-derived object is, no more than it can from the pointer.
Consider:
void DoSomething(const Base& b) { std::cout << b.getName(); }
Base base;
DoSomething(base); // b is bound to Base object. Prints "Base"
Derived derived;
DoSomething(derived); // b is bound to Base subobject of Derived object.
// Prints "Derived"
I have implemented a custom container (same vein as std::vector) and I am trying to make it so that its 'push_back' function would use leverage on move semantics to avoid creating a copy of whatever is being pushed back - specially when the object to be pushed into the container is returned by an external function.
After reading quite a bit about move semantics and custom containers, I still can't find why my approach is still generating a copy instead of just moving the passed object into the container's inner dynamic array.
Here is a simplified version of my container looks like:
template<class T>
class Constructor
{
private:
size_t size = 0;
size_t cap = 0;
T *data = nullptr;
public:
Constructor()
{
cap = 1;
size = 0;
data = static_cast<T*>(malloc(cap * sizeof(T)));
}
~Constructor()
{ delete[] data; }
template<typename U>
void push_back(U &&value)
{
if (size + 1 >= cap)
{
size_t new_cap = (cap * 2);
T* new_data = static_cast<T*>(malloc(new_cap * sizeof(T)));
memmove(new_data, data, (size) * sizeof(T));
for (size_t i = 0; i<cap; i++)
{
data[i].~T();
}
delete[] data;
cap = new_cap;
data = new_data;
new(data + size) T(std::forward<U>(value));
}
else
{
new(data + size) T(std::forward<U>(value));
}
++size;
}
const T& operator[](const size_t index) const //access [] overloading
{
return data[index];
}
};
Here is a custom class that will print messages when its instances are created, copied or moved, in order to help debugging:
class MyClass
{
size_t id;
public:
MyClass(const size_t new_id)
{
id = new_id;
std::cout << "new instance with id " << id << std::endl;
}
MyClass(const MyClass &passedEntity)
{
id = passedEntity.id;
std::cout << "copied instance" << std::endl;
}
MyClass(MyClass &&passedEntity)
{
id = passedEntity.id;
std::cout << "moved instance" << std::endl;
}
void printID() const
{
std::cout << "this instance's id is " << id << std::endl;
}
};
And here is the external function:
MyClass &foo(MyClass &passed)
{
return passed;
}
Lastly, here is the main function that runs a test case using the above function and classes to show the problem:
int main()
{
MyClass a(33);
std::cout << std::endl;
std::cout << "Using my custom container: " << std::endl;
Constructor<MyClass> myContainer;
myContainer.push_back(foo(a));
myContainer[0].printID();
std::cout << std::endl;
std::cout << "Using dinamic array: " << std::endl;
MyClass *dinArray = static_cast<MyClass*>(malloc(1 * sizeof(MyClass)));
dinArray = new(dinArray + 1) MyClass(std::forward<MyClass>(foo(a)));
dinArray[0].printID();
std::cout << std::endl;
system("Pause");
return 0;
}
The output is:
new instance with id 33
Using my custom container:
copied instance
this instance's id is 33
Using dinamic array:
moved instance
this instance's id is 33
As it can be seen, if the instance of MyClass is put directly into a dynamic array, then just the move conmstructor is called and not the copy constructor. However, if I push_back the yClass instance into an instance of Container, a copy constructor is still being called.
Could someone help me understand what exactly am I doing wrong here? How could I make it so that elements are pushed into the container without generating a copy?
When you call this line
myContainer.push_back(foo(a));
L-value is passed into push_back method, and now read about using std::forward - http://www.cplusplus.com/reference/utility/forward/,
Returns an rvalue reference to arg if arg is not an lvalue reference.
If arg is an lvalue reference, the function returns arg without modifying its type.
and in your push_back you call
new(data + size) T(std::forward<U>(value));
but value was passed as L-value, and only constructor MyClass(const MyClass &passedEntity) can be invoked.
If you want a object to be moved, you can write
myContainer.push_back(std::move(a)); // cast to R-reference
EDIT
You should not use move in your push_back function, below is simple example.
Suppose you have class like this:
struct Foo {
int i;
Foo (int i = 0) : i(i) {
}
~Foo () {
}
Foo (const Foo& ) {
}
Foo& operator=(const Foo&) {
return *this;
}
Foo (Foo&& f)
{
i = f.i;
f.i = 0; // this is important
}
Foo& operator=(Foo&& f)
{
i = f.i;
f.i = 0; // this is important
return *this;
}
};
we have also 2 functions
template<class T>
void process1 (const T& ) {
cout << "process1" << endl;
}
template<class T>
void process (T&& obj) {
cout << "process2" << endl;
T newObj = forward<T>(obj);
}
and bars functions are counterparts of your push_back method.
template <typename T>
void bar1 (T&& value) {
process (move(value)); // you use move in your push_back method
}
template <typename T>
void bar2 (T&& value) {
process (forward<T>(value));
}
now we must consider 4 cases:
[1] pass L-value, version with forward
Foo f(20);
bar2 (f);
cout << (f.i) << endl; // 20
[2] pass R-value, version with forward
Foo f(20);
bar2 (move(f));
cout << (f.i) << endl; // 0, it is OK bacuse we wanted to move 'f' object
[3] pass R-value, version with move
Foo f(20);
bar1 (move(f));
cout << (f.i) << endl; // 0, ok, we wanted to move 'f' object
[4] pass L-value, version with move in your push_back method
Foo f(20);
bar1 (f);
cout << (f.i) << endl; // 0 !!! is it OK ? WRONG
in last case, we passed f as L-value, but this object was moved in bar1 function, for me this is strange behavior and is incorrect.
It is not safe to perform a memmove with objects in C++.
You can find more information here Will memcpy or memmove cause problems copying classes?
If this is C++11 onwards, then what you want to use is placement new and the move constructor. (you could probably just bin the placement new though unless you really want to keep allocating memory yourself)
If this is any other version of C++ then you'll have to just accept that that either you're going to have to copy the object (like the rest of the stl) or that your object will have to implement a function like void moveTo(T& other)
Why is the following code calling a's copy constructors?
class a {
public:
a(int x) : x_(x) { std::cout << "a constructor" << std::endl; }
a(a&& a_) { std::cout << "a move constructor" << std::endl; }
a(const a& a_) { std::cout << "a copy constructor" << std::endl; }
private:
int x_;
};
class b {
public:
b(std::vector<a>&& v) : v_(std::move(v)) {}
private:
std::vector<a> v_;
};
int main() {
b s({2, 3, 4, 5, 6});
}
The output is the following:
a constructor
a constructor
a constructor
a constructor
a constructor
a copy constructor
a copy constructor
a copy constructor
a copy constructor
a copy constructor
I was expecting no copy since the vector is created in place and passed as a rvalue reference and after that moved. What's actually happening?
std::initializer_list<T> is a wrapper around an array of const T objects. (More precisely, std::initializer_list<T>::reference is const T&). Notice the const. It has to be like that, because its elements can be literals.
This means that the constructor of std::vector taking std::initializer_list has to copy the elements from the list into the vector, it cannot move them.
Can I have class/struct with rvalue field in c++11?
Like this one:
template<typename T>
struct RvalueTest{
RvalueTest(T&& value) : value( std::forward<T>(value) ){}
T&& value;
};
Because in the following test:
class Widget {
public:
Widget(){std::cout << "Widget ctor " << std::endl; }
Widget(int h) : h(h){std::cout << "Widget ctor param " << std::endl; }
Widget(const Widget&) { std::cout << "Widget copy ctor " << std::endl; }
Widget(Widget&&) { std::cout << "Widget move ctor " << std::endl; } // added this
template<typename T>
Widget(const T&) { std::cout << "Generalized Widget copy ctor " << std::endl; }
template<typename T>
Widget(T&&) { std::cout << "Universal Widget ctor " << std::endl; }
int h;
};
RvalueTest<Widget> r(Widget(12));
std::cout << r.value.h;
I got some trash value at output (with -O2):
http://coliru.stacked-crooked.com/a/7d7bada1dacf5352
Widget ctor param
4203470
And right value with -O0:
http://coliru.stacked-crooked.com/a/f29a8469ec179046
Widget ctor param
12
WHY???
P.S. What I try to achive is a single ctor call, without any additional move/copy constructors.
UPDATED
It compiles ok with clang http://coliru.stacked-crooked.com/a/6a92105f5f85b943
GCC bug? Or it works as it should ?
The problem is not specific to rvalue references. You will see the same odd behaviour, if you replace T&& by const T&.
template<typename T>
struct ReferenceTest{
ReferenceTest(const T& value) : value(value){}
const T& value;
};
The problem is, that you are storing a reference to a temporary object. The temporary object is automatically destroyed at the end of the statement it was created, but you try to access it later via the reference.
RvalueTest<Widget> r(Widget(12)); // pass reference to temporary object to constructor
std::cout << r.value.h; // invalid reference
It is possible to use rvalue references as member variables. However, you have to be careful if doing so. An example from the standard library is std::forward_as_tuple.
auto t = std::forward_as_tuple(42);
// t has type std::tuple<int&&>
Regarding the question how to avoid the copy or move construction: Don't use rvalue reference member variables. Here are two approaches, you can use instead:
std::unique_ptr: If copying or moving a Widget is too expensive, you can use a smart pointer:
template <typename T>
struct Test
{
std::unique_ptr<T> _value;
explicit Test(std::unique_ptr<T> value) : _value{std::move(value)} { }
};
Test<Widget> t{std::make_unique<Widget>(12)};
std::cout << t._value->h;
The second approach creates the Widget in-place. That's what the different emplace functions of the standard containers do, e.g. std::vector::emplace_back.
template <typename T>
struct Test
{
T _value;
template <typename ...Args>
explicit Test(Args&&... args) : _value{std::forward<Args>(args)...} { }
};
Test<Widget> t{12};
std::cout << t._value.h;