We can create an object in two ways:
myClass myObject = myClass(123);
//or
myClass myObject(123);
Are there any differences in background between these two? I want to use the first one but it seems like combining these two lines:
myClass myObject;
myObject= myClass(123);
Does the second one also do the same thing?
myClass myVariable = myClass(123);
is copy initialization.
myClass myVariable(123);
is direct initialization.
myClass myVariable;
myVariable = myClass(123);
is default initialization followed by copy (or move) assignment.
Typically, the first two are identical because of copy elision. The relevant rule can be found in [class.copy]/31 (N4140, C++14 draft standard):
When certain criteria are met, an implementation is allowed to omit
the copy/move construction of a class object [...]:
— when a temporary class object that has not been bound to a reference
(12.2) would be copied/moved to a class object with the same
cv-unqualified type, the copy/move operation can be omitted by
constructing the temporary object directly into the target of the
omitted copy/move
Not that I know of, in the end. You have described three ways of defining an object and all 3 ways place the object on top of the stack, calling the same constructor. The functionality would be different if you used the new operator.
This one does not use the assignment operator (the two lines are equivalent, as far as I know). = is used in the syntax but operator= is not actually used:
myClass myVariable = myClass(123);
//or
myClass myVariable(123);
This one uses the assignment operator:
myClass myVariable;
myVariable = myClass(123);
If assignment operator is badly or not implemented, first statement works, second may (and will most likely) crash.
#include <iostream>
#include <string.h>
using namespace std;
class Dvector
{
public:
Dvector( int thesize = 0 )
{
std::cout << "Constructing object of size " << thesize << std::endl;
size = thesize;
data = new double[size];
}
Dvector( const Dvector& v )
{
std::cout << "Constructing object of size " << v.size << " by copy" << std::endl;
size = v.size;
data = new double[size];
memcpy( data, v.data, sizeof(double)*size );
}
Dvector& operator=( const Dvector& v )
{
std::cout << "Assigning object of size " << v.size << std::endl;
if ( &v != this )
{
size = v.size;
data = new double[size];
memcpy( data, v.data, sizeof(double)*size );
}
return *this;
}
~Dvector()
{
std::cout << "Destroying object" << std::endl;
delete [] data;
}
private:
double* data;
int size;
};
int main() {
Dvector v = Dvector(3);
return 0;
}
Displays:
Constructing object of size 3
Destroying object
When:
int main() {
Dvector v;
v = Dvector(3);
return 0;
}
Displays:
Constructing object of size 0
Constructing object of size 3
Assigning object of size 3
Destroying object
Destroying object
And would have crashed if copy constructor was not defined...because then v.data ends up pointing to data allocated by temporary variables (Dvector(3)) and then deleted. Possible crash when trying to access v.data or upon v destruction (deleting already freed memory).
Related
Say there is an object with automatic storage duration, and that object is copy-initialized from within a nested scope, like a loop body. Is the lifetime of the values created from within the nested scope extended into the containing scope?
#include<iostream>
using namespace std;
class Thing {
public:
int data;
Thing(int data) : data(data) { cout << "making a thing" << endl; }
~Thing() { cout << "destroying a thing" << endl; }
};
int main() {
Thing t = Thing(-1);
for (int i = 0; i < 4; i++) {
t = Thing(i); // this is both created AND destroyed from within this scope...?
}
cout << t.data << endl; // undefined behavior?
}
Right now, accessing t.data at the end works, but I see that the destructor for each Thing is invoked once per loop iteration, so I might just be getting lucky?
This looks relevant (but I am not a lawyer so it's tough to decipher): some 2011 c++ standard
specifically:
For such an object [with automatic storage duration] that does not have a variable length array type, its lifetime extends from entry into the block with which it is associated until execution of that block ends in any way...
So if my code snippet is undefined behavior - for a loop body to change a local variable, that local variable should be manually heap allocated and later manually released, or...?
This is perfectly legal, defined behavior.
Your mistake is in thinking that the lifetime of the Thing created inside the block matters. It doesn't. After the copy, the copy-assigned t hasn't changed what it is; it's still the same t created outside the block, it just had its values updated. Its lifetime is entirely unaffected. In the context of your quote, the "block with which it[t] is associated" is the top-level function scope (where it was declared), not the block in which it was copy-assigned.
The Thing created inside the block does expire each time, but that's fine; it was copied from, then never used again.
There is no undefined behavior here:
#include<iostream>
class Thing {
public:
int data;
Thing(int data) : data(data) { std::cout << "making a thing" << data << std::endl; }
~Thing() { std::cout << "destroying a thing" << data << std::endl; }
Thing& operator=(const Thing &t) { data = t.data; std::cout << "operator=" << std::endl; return *this; }
};
int main() {
Thing t = Thing(-1); // create object t
for (int i = 0; i < 4; i++) {
t = Thing(i);
// 1. create an object Thing(i)
// 2. use operator= to copy that object into the object t
// 3. Thing(i) object gets destroyed at the end of scope
}
// object t is still valid, but it was modified in the loop with operator=
cout << t.data << endl; // no undefined behavior here
}
You can add copy-assignment operator (operator=) member function into the Thing class to check this. In your case this operator was implicitly added by the compiler, according to the rules in the standard:
If no user-defined copy assignment operators are provided for a class
type (struct, class, or union), the compiler will always declare one
as an inline public member of the class. This implicitly-declared copy
assignment operator has the form T& T::operator=(const T&) if all of
the following is true:
each direct base B of T has a copy assignment operator whose
parameters are B or const B& or const volatile B&
each non-static data
member M of T of class type or array of class type has a copy
assignment operator whose parameters are M or const M& or const
volatile M&
I am pretty new to the C++ world...
I have a question about destructors in C++.
If there is a class like:
class TestClass {
public:
double *array;
}
When I put a pointer of one array to *array and put again another and delete Testclass instance, then I can no longer access the older one?
It becomes an error:
int main() {
TestClass tc;
tc.array = new double[10];
double *array2 = tc.array;
tc.array[3]=9;
tc.array = new double[10];
delete &tc; // <------------------- this one.
std::cout<<array2[3]<<std::endl;
// std::cout<<tc.array[3]<<array2[3]<<std::endl;
}
If there is no delete &tc and activate the commentline (the last line),
'09' shows up, (tc.array[3] != array2[3] => tc.array != array2)
which does not mean tc.arrray is not the one in *array2 ??
What is wrong?
The tc is a function-scoped variable (allocated on the stack). It's not allocated with new. You can't delete it. it will be freed automatically when the function return
Dealing with raw pointers is somewhat tricky and your class would probably benefit from using a std::vector<double> instead of a raw pointer. Another choice could be a std::unique_ptr<double[]> that handles deleting the pointer for you.
That said, if you want to try it out, you should know when and what to delete.
tc is an automatic variable. You didn't create tc using new so you should not delete it. It will be destroyed when it goes out of scope - when the program leaves the block in which tc was created.
You haven't defined a destructor for TestClass so the memory to which array points will still be allocated even after tc is destroyed. The destructor would typically look like this:
~TestClass() {
delete[] array;
}
You also assign directly to tcs internal pointer, which is dangerous. You should have just as many delete[] as you have new[]s, which is not the case in your program, so it'll leak. You should typically hide the internal pointer so it can't be reassigned without the object owning it having control.
For manual memory management to work properly, there are some member functions you should consider:
Copy constructor
Move constructor
Copy assignment operator
Move assignment operator
Destructor
You can read more about those here: The rule of three/five/zero
Here's an example of what your class could look like with the 5 member functions mentioned above plus a default constructor, a converting constructor taking a pointer as parameter and two subscript operators.
#include <iostream>
#include <utility> // std::move, std::swap, std::exchange
class TestClass {
public:
// default constructor
TestClass() :
array(nullptr) // member initializer
{
std::cout << "default ctor\n";
}
// converting constructor - refuse lvalue pointers since we want to make sure
// that we "own" the pointer
TestClass(double*&& p) : array(p) {
std::cout << "converting ctor " << p << "\n";
}
// copy constructing without knowledge about the size of the array isn't possible
TestClass(const TestClass&) = delete;
// move construction works though
TestClass(TestClass&& rhs) :
array(std::exchange(rhs.array, nullptr)) // steal pointer from rhs
{
std::cout << "move ctor\n";
}
// copy assignment without knowledge about the size of the array isn't possible
TestClass& operator=(const TestClass&) = delete;
// move assignment works though
TestClass& operator=(TestClass&& rhs) {
std::cout << "move assignment\n";
// swap pointers with rhs - let rhs delete[] our current pointer
std::swap(array, rhs.array);
return *this;
}
~TestClass() { // destructor
delete[] array;
}
// subscripting support
double& operator[](size_t idx) { return array[idx]; }
double operator[](size_t idx) const { return array[idx]; }
private: // hide your raw pointer from direct access
double* array;
};
void printer(const TestClass& tc, size_t idx) {
// use const version of operator[] in the class
std::cout << tc[idx] << "\n";
}
int main() {
TestClass tc; // default ctor
// use the converting constructor that creates a TestClass object from a pointer.
// It is then move assigned to tc
tc = new double[10];
tc[3] = 1; // use the non-const version of operator[] in the class
printer(tc, 3);
double* a = new double[10];
// tc = a; // this won't work since we don't accept lvalues in assignment
tc = std::move(a); // make an xvalue to allow the assignment. "a" should not be
// delete[]-ed after this since we granted tc the possibillity
// to take ownership the pointer.
tc[3] = 2;
printer(tc, 3);
}
Language: C++
My question is about whether the copy constructor or the assignment operator is called in the following situations. To precede, I understand the following:
MyClass a(3); // single param constructor
MyClass b(a); // copy constructor invoked
MyClass c = b; // copy constructor invoked
MyClass d; // default constructor
d = c; // assignment operator invoked
However, I was hoping someone could give a similar breakdown for these:
1) For lines 2-3, is assignment operator or copy constructor called?
MyClass arr[10];
arr[2] = a;
arr[5] = MyClass(1);
2) Constructor, then copy constructor? Or constructor, then assignment operator?
MyClass arr2[] = {MyClass(), MyClass(9)};
3) Assuming vector v's internal representation has space for one more object, is new element copied using assignment operator or copy constructor?
std::vector<MyClass> v;
v.push_back(MyClass(2));
...
...
4) Assuming vector v's internal representation is out of space and must realloc, are old elements copied using assignment operator, or copy constructor?
v.push_back(MyClass(2)); // Assuming vector is out of space; must realloc
MyClass arr[10];
Constructor for MyClass is called 10 times, as 10 objects of arr are created.
arr[2] = a;
Assignment operator is invoked, assigns arr[2] to a.
arr[5] = MyClass(1);
First single param constructor with parameter 1 is called and creates an object of MyClass. Then assignment operator is invoked.
MyClass arr2[] = {MyClass(), MyClass(9)};
Two constructors and only are called here. First the Myclass() and then "single param constructor" MyClass(9). The array declaration with initialization is not an assignment, cause no existing arrays members exists to assign to.
std::vector<MyClass> v;
Constructor for std::vector<MyClass> is called.
v.push_back(MyClass(2));
std::vector::push_back creates a copy of the class and stores it. So first MyClass(2) constructor is called, then copy constructor MyClass(const MyClass &) is called to copy the value. Then the copied object is stored.
Assuming vector v's internal representation is out of space and must realloc, are old elements copied using assignment operator, or copy constructor?
Copy operator is invoked for each member. So:
std::vector<MyClass> a;
Calls constructor for std::vector<MyClass>
a.push_back(MyClass(1));
Calls constructor for MyClass(1) and copies it using copy cosntructor MyClass(MyClass&).
After that if we add another element to the array:
a.push_back(MyClass(2));
Then MyClass(2) constructor is called, then copy constructor is called for MyClass(MyClass&) for the just constructed MyClass(2) object. Then the vector copy-constructs all existing members from the ned, so for the pre-existing object in the vector MyClass(1) already upped copy constructor is called.
Really, play with it a little. And insert cout everywhere to see it:
struct MyClass {
MyClass(int a = 0) : _a(a) {
_count = count++;
std::cout << __func__ << " "
<< _count << " "
<< this << " "
<< this->_a << "\n";
}
MyClass(const MyClass &old_obj) {
this->_a = old_obj._a;
std::cout << __func__ << "Copy "
<< _count << " "
<< this << " "
<< this->_a << " "
<< &old_obj << " "
<< old_obj._a << "\n";
}
void operator=(const MyClass &old_obj) {
this->_a = old_obj._a;
std::cout << "MyClass" << __func__ << " "
<< _count << " "
<< this << " "
<< this->_a << " "
<< &old_obj << " "
<< old_obj._a << "\n";
}
static int count;
int _count;
int _a;
};
int MyClass::count = 0;
When you have
type variable_name = some_value
then you are declaring a variable and will always be calling its constructor (if it has one). This is called copy initialization and this will never be assignment
So, in
MyClass arr[10]; // 1
arr[2] = a; // 2
arr[5] = MyClass(1); // 3
Line 1 creates an array of 10 MyClass and default constructs each of them. Lines 2 and 3 are assignment.
In
MyClass arr2[] = {MyClass(), MyClass(9)};
You initialize an array of 2 objects using the values in the *braced-init-list` as the initializers for the array members. There are many rules governing list initialization but the one thing they have in common is no assignment will happen, only constructor calls.
With
std::vector<MyClass> v;
v.push_back(MyClass(2));
Assuming the vector doesn't reallocate, you have a constructor call for MyClass(2) and then the element in the vector is copy constructed from that temporary object. If the vector has to grow then all of the current elements are copy/move constructed to a new buffer then the temporary is copied constructed at the end.
In certain embedded situations, memory needs to be moved with memcopy style functions (such as from external memory, or using closed API calls).
When such a C++ object needs to be moved this way, however it doesn't have a default constructor, you can't do something like this, :
class Object {
//local data
public:
Object(/* not a default constructor */){}
}
//elsewhere:
Object o; //compiler will complain here...
memcpy_like_function(src_address, &o, sizeof(o));
because Object doesn't have a default constructor, and thus the compiler will complain about creating Object o.
Some notes from things that have shown up in the comments:
memcpy_like_function is like memcpy, it isn't actually memcpy. The src_address isn't a pointer to an address I can reach, or an int representing a pointer to an address I can reach. It is an int representing an address in a memory space I can't reach. The only way for me to access this memory space is with this function.
Object doesn't have a default constructor, has no virtual functions, is neither inherited from, nor inherits anything. Object is trivially copyable.
What is the correct way to deal with creating such an object in this situation, without putting it on the heap? Preferably, I would like to get a stack allocated object that will behave correctly with scope and destructors. For the purposes of this question, Object is not inheriting from anything.
This seems like a horribly bad idea, but assuming that your memcpy_like_function actually works, then you can just add a constructor to Object
class Object {
//local data
public:
Object(void* src_address)
{
memcpy_like_function(src_address, this, sizeof(*this));
}
};
//elsewhere:
Object o(src_address);
because Object doesn't have a default constructor
When Object doesn't have a default constructor,
//Object o;//NG
there is no way to construct Object unless call another ctor or factory function. Because of that, you cannot call memcpy-like function.
When you have way to construct Object, to use memcpy-like function, Object class must grantee that it is trivially copyable class and standard-layout class(not equal to POD class).
trivial class : trivially copyable class && has no default user-defined constructor
POD class : trivial class && standard-layout class
You cannot safely copy an object using a memcpy-like function unless the object is a POD type. If the object is a POD type, you should be able to use:
char destination[sizeof(Object)];
memcpy_like_function(src_address, destination, sizeof(destination));
Object* ptr = reinterpret_cast<Object*>(destination);
My gut-feel says that that should work under all compilers for POD types. Whether it is cause for undefined behavior under some rules of the standard, I am not sure.
If that is cause for undefined behavior under some rules of the standard, you won't be able to save a POD-type to a binary file and read it from the binary file in a standards compliant manner. They rely on the bit-pattern written to a file and read from a file to represent the object.
The following program produces the expected result under g++.
#include <iostream>
#include <cstring>
struct Object
{
int i;
double d;
Object(int ii, double dd) : i(ii), d(dd) {}
};
int main()
{
Object o1(10, 20.34);
char dest[sizeof(Object)];
memcpy(dest, &o1, sizeof(dest));
Object* ptr = reinterpret_cast<Object*>(dest);
std::cout << o1.i << ", " << o1.d << std::endl;
std::cout << ptr->i << ", " << ptr->d << std::endl;
}
Update, in response to OP's comments
The following program works as expected under g++.
#include <iostream>
#include <cstring>
struct Object
{
int i;
double d;
Object(int ii, double dd) : i(ii), d(dd) {}
};
Object testFunction(Object o1)
{
char dest[sizeof(Object)];
memcpy(dest, &o1, sizeof(dest));
Object* ptr = reinterpret_cast<Object*>(dest);
return *ptr;
}
int main()
{
Object o1(10, 20.34);
Object o2 = testFunction(o1);
std::cout << o1.i << ", " << o1.d << std::endl;
std::cout << o2.i << ", " << o2.d << std::endl;
o2.i = 25;
o2.d = 39.65;
std::cout << o2.i << ", " << o2.d << std::endl;
}
What you could do is simply use an array of bytes, then cast.
Note: what you are doing is not really good C++ practice, typically you should use assignment operators or copy constructors, and you should stick to the safer C++ casts rather than a brute-force C-style cast.
Anyway, that being said, this code will work:
class Object {
int i;
public:
Object(int i) : i(i) {}
void foo() const {
std::cout << i << std::endl;
}
};
void bla() {
Object one(1);
char *bytes = new char[sizeof(Object)];
memcpy(bytes, &one, sizeof(Object));
Object &anotherOne = (Object &) *bytes;
anotherOne.foo();
const Object &oneMore = (Object) *bytes;
oneMore.foo();
Object *oneMoreTime = (Object *) bytes;
oneMoreTime->foo();
delete[] bytes;
}
The output is:
1
1
1
In summary, you need to allocate a region of memory on the stack or the heap that will become the Object instance.
I have the following code to test out my understanding of basic pointers in C++:
// Integer.cpp
#include "Integer.h"
Integer::Integer()
{
value = new int;
*value = 0;
}
Integer::Integer( int intVal )
{
value = new int;
*value = intVal;
}
Integer::~Integer()
{
delete value;
}
Integer::Integer(const Integer &rhInt)
{
value = new int;
*value = *rhInt.value;
}
int Integer::getInteger() const
{
return *value;
}
void Integer::setInteger( int newInteger )
{
*value = newInteger;
}
Integer& Integer::operator=( const Integer& rhInt )
{
*value = *rhInt.value;
return *this;
}
// IntegerTest.cpp
#include <iostream>
#include <cstdlib>
#include "Integer.h"
using namespace std;
void displayInteger( char* str, Integer intObj )
{
cout << str << " is " << intObj.getInteger() << endl;
}
int main( int argc, char* argv[] )
{
Integer intVal1;
Integer intVal2(10);
displayInteger( "intVal1", intVal1 );
displayInteger( "intVal2", intVal2 );
intVal1 = intVal2;
displayInteger( "intVal1", intVal1 );
return EXIT_SUCCESS;
}
This code works exactly as expected as is, it prints out:
intVal1 is 0
intVal2 is 10
intVal1 is 10
However if I remove the copy constructor it prints out something like:
intVal1 is 0
intVal2 is 10
intVal1 is 6705152
I don't understand why this is the case. My understanding is that the copy constructor is used when the assignment is to an object that doesn't exist. Here intVal1 does exist, so why isn't the assignment operator called?
The copy constructor is not used during assignment. Copy constructor in your case is used when passing arguments to displayInteger function. The second parameter is passed by value, meaning that it is initailized by copy contructor.
Your version of copy constructor performs deep copying of data owned by the class (just like your assignment operator does). So, everything works correctly with your version of copy constructor.
If you remove your own copy constructor, the compiler will generate one for you implicitly. The compiler-generated copy constructor will perform shallow copying of the object. This will violate the "Rule of Three" and destroy the functionality of your class, which is exactly what you observe in your experiment. Basically, the first call to displayInteger damages your intVal1 object and the second call to displayInteger damages your intVal2 object. After that both of your objects are broken, which is why the third displayInteger call displays garbage.
If you change the declaration of displayInteger to
void displayInteger( char* str, const Integer &intObj )
your code will "work" even without an explicit copy constructor. But it is not a good idea to ignore the "Rule of Three" in any case. A class implemented in this way either has to obey the "Rule of Three" or has to be made non-copyable.
The problem you're experiencing is caused by the default copy constructor, which copies the pointer but doesn't associate it with newly allocated memory (like your implementation of copy constructor does). When you pass object by value, a copy is created and when the execution goes out of scope, this copy is destructed. delete from the destructor invalidates the value pointer of intVal1 object, making it dangling pointer, dereferencing of which causes undefined behavior.
Debug outputs might be used to understand the behavior of your code:
class Integer {
public:
Integer() {
cout << "ctor" << endl;
value = new int;
*value = 0;
}
~Integer() {
cout << "destructor" << endl;
delete value;
}
Integer(int intVal) {
cout << "ctor(int)" << endl;
value = new int;
*value = intVal;
}
Integer(const Integer &rhInt) {
cout << "copy ctor" << endl;
value = new int;
*value = *rhInt.value;
}
Integer& operator=(const Integer& rhInt){
cout << "assignment" << endl;
*value = *rhInt.value;
return *this;
}
int *value;
};
void foo(Integer intObj) {
cout << intObj.value << " " << *(intObj.value) << endl;
}
Now output of this code:
Integer intVal1;
Integer intVal2(10);
foo( intVal1 );
foo( intVal2 );
intVal1 = intVal2;
foo( intVal1 );
is:
ctor
ctor(int)
copy ctor
0x9ed4028 0
destructor
copy ctor
0x9ed4038 10
destructor
assignment
copy ctor
0x9ed4048 10
destructor
destructor
destructor
which shows that copy constructor is used when passing objects by value. However, important to notice here is the destructor called upon the return from your function. And if you remove your implementation of copy constructor, then the output is:
ctor
ctor(int)
0x8134008 0
destructor
0x8134018 10
destructor
assignment
0x8134008 135479296
destructor
destructor
destructor
showing that the first copy called delete on the same pointer (pointing to 0x8134008) as was used by third copy later, where the memory pointed by this dangling pointer has been used.
Think about this call:
displayInteger( "intVal1", intVal1 );
You are creating a copy of intVal1 into the intObj parameter of displayInteger:
void displayInteger( char* str, Integer intObj )
{
cout << str << " is " << intObj.getInteger() << endl;
}
That copy will be pointing to the same int that intVal1 is. When displayInteger returns, intObj is destroyed, which will cause the int to be destroyed, and the pointer in intVal1 to be pointing to an invalid object. At that point all bets are off (A.K.A. undefined behavior) if you try to access the value. A similar thing happens for intVal2.
At a more general level, by removing the copy constructor, you are violating the Rule of Three, which typically leads to these kinds of problems.