After working 15 years in C++ I found that I don't understand references completely.
class TestClass
{
public:
TestClass() : m_nData(0)
{
}
TestClass(int n) : m_nData(n)
{
}
~TestClass()
{
cout << "destructor" << endl;
}
void Dump()
{
cout << "data = " << m_nData << " ptr = 0x" << hex << this << dec << endl;
}
private:
int m_nData;
};
int main()
{
cout << "main started" << endl;
TestClass& c = TestClass();
c.Dump();
c = TestClass(10);
c.Dump();
cout << "main ended" << endl;
return 0;
}
// prints:
// main started
// data = 0 ptr = 0x0012FF54
// destructor
// data = 10 ptr = 0x0012FF54
// main ended
// destructor
I understand from this test that TestClass instance is created on the stack (is this correct?) and initialized by first TestClass constructor. When is this instance allocated: when the main function is loaded or when the reference assignment is executed? When it is destroyed?
After the second reference assignment, the object address is not changed. Does this mean that the destructor and constructor are applied to the same memory area? Or is the memory deallocated (dynamically? on the stack?) and allocated again?
I know everything about stack and heap-allocated objects lifetime, their constructors and destructors, but I cannot understand what exactly happens in this program.
Edit:
Thanks to all. I tried to reproduce in this test some other (more complicated) program behavior. Your comments helped me to understand both my mistake and another program I am fighting with...
Fixed code is:
int main()
{
cout << "main started" << endl;
TestClass t;
TestClass& c(t);
c.Dump();
c = TestClass(10);
c.Dump();
cout << "main ended" << endl;
return 0;
}
Note from 29.06.2022: After latest edition by Daniel Walker this question looks like complete crap. I am not responsible for this.
Your code suffers from multiple problems and ultimately won't make sense. However, let's hack through it.
1) You can only bind a temporary to a const reference, thus extending its lifetime:
const TestClass & c = TestClass();
2) Now we can't use dump, because you didn't declare it const:
void Dump() const
3) Saying c = TestClass() is an assignment. However, c is now a reference-to-const, which cannot be assigned to, since assignment is non-constant (for obvious reasons). Let's hack around this:
const_cast<TestClass&>(c) = TestClass(10);
Now we've assigned a new value to the temporary-but-extended object c, and all is as it should be:
main started
data = 0 ptr = 0x0xbfa8219c
destructor
data = 10 ptr = 0x0xbfa8219c
main ended
destructor
The pointers are the same because there's only one object, namely the (temporary) one referenced by c. Assigning to it is a hack that's undefined behaviour in general, but we get away with it for the purpose of this demonstration.
The intermediate destructor is that of the second temporary TestClass(10).
TestClass& c = TestClass(); // TestClass() temporary doesn't persist beyond this expression.
c.Dump();
TestClass() creates a temporary and you cannot take the reference of it.
const TestClass& c = TestClass();
const qualification extends the life time of the temporary being created until the scope of the object c.
TestClass& c = TestClass();
This wouldn't even compile!
Attempting to bind a temporary to non-const reference would result in compilation error.
However, you can bind a temporary to const reference:
{
const TestClass& c = TestClass();
//use c
//....
}//<-------- the temporary will be destroyed here.
In this case, the life of the temporary extends to the lifetime of the reference, i.e when the reference variable goes out of scope, the temporary will be destroyed as shown above.
1) you can't get not const reference to a temporary object
2) in the line c = TestClass(10); operator=(...) is called
A good way is to compare references to pointers... (references are usually implemented the same way in assembly generally by using the ebx register). The main difference is that reference is constant after initialization...
However, The line const TestClass& c = TestClass(); is parallel to const TestClass* const pc = &TestClass(); so the object will be create and destroyed on the stack, pc will still hold the same address.
Related
I very recently started getting into C++, my main language has always been Java.
I want to be able to pass a reference to an object in a constructor so the object constructed can use the passed object and make changes to it.
Doing some research on the subject I've come across copy constructors but I haven't quite understood if they are strictly needed for this operation and what they really accomplish in the first place.
TLDR: I want to learn how to do the equivalent of the following Java code in C++.
Java code
class Master {
private Slave slave;
Master(Slave slave) {
this.slave = slave;
}
public void doSomethingToSlave() {
slave.doSomething();
}
public void changeSlave(Slave s) {
this.slave = s;
}
}
C++ Code ??? -my attempt so far-
class Master {
public:
Master(Slave& slave);
void doSomethingToSlave();
void changeSlave(Slave& s);
private:
Slave& slave; // Is this correct? Do I need to specify that it's a reference?
}
Master::Master(Slave& slave) { // Copy constructor needed?
this->slave = slave;
}
Master::doSomethingToSlave() {
slave.doSomething();
}
Master::changeSlave(Slave& s) {
slave = s;
}
Using Pointers ?
class Master {
public:
Master(Slave* slave);
void doSomethingToSlave();
void changeSlave(Slave* s);
private:
Slave* slave;
}
Master::Master(Slave* slave) {
this->slave = slave;
}
Master::doSomethingToSlave() {
slave.doSomething(); // How do I access the object at slave*?
}
Master::changeSlave(Slave* s) {
slave = s;
}
Here's an example which I hope elucidates the behavior:
#include <iostream>
using namespace std;
class MyType {
public:
int value;
};
int main() {
// value == 1
MyType obj1{1};
// initialize a reference to refer to obj1.
MyType &ref = obj1;
// value == 2
MyType obj2{2};
// ref refers to obj1.
cout << "A. ref.value = " << ref.value << endl;
// this actually reassigns the memory in obj1
// to the memory from obj2.
ref = obj2;
cout << "B. obj1.value = " << obj1.value << endl;
// value == 3
MyType obj3{3};
// initialize a pointer to point to obj3.
MyType *ptr = &obj3;
// ptr refers to obj3.
cout << "C. ptr->value = " << ptr->value << endl;
// now we're just reassigning the pointer value.
// this is like Java reference assignment.
// ptr now points to obj2.
ptr = &obj2;
cout << "D. obj3.value = " << obj3.value << endl;
// now we're reassigning the memory at obj2 to
// the memory from obj1.
// this is what the C++ reference assignment is doing.
*ptr = obj2;
cout << "E. obj2.value = " << obj2.value << endl;
return 0;
}
The output is as follows:
A. ref.value = 1
B. obj1.value = 2
C. ptr->value = 3
D. obj3.value = 3
E. obj2.value = 2
A Java reference is actually like a C++ pointer, but Java doesn't let you do the dereference and assignment *ptr = value.
However, I agree with what others are saying that approaching C++ with the coding style of Java isn't really a good idea. Modern C++ programs don't really use raw pointers. They use smart pointers or value objects, because C++ doesn't have garbage collection. If you're passing raw pointers around, it's difficult to figure out exactly when a particular object needs to be destroyed.
In your case, I think you'd use a std::shared_ptr<Slave>.
A value object is like Slave slave;, which is a stack-allocated object if it's declared in a method body, or it's stored directly inside the memory for an object if it's declared as a member of a class or struct.
A copy constructor (ctor) is used to copy an object, i.e. to create a copy of the object of the same type, given as argument.
In your case,
Master::Master(Slave& slave)
is not a copy ctor, and a copy ctor is not required for Master.
However, Slave should have a copy ctor and an assignment operator due to
Master::changeSlave(Slave& s) {
slave = s;
}
This is because you are assigning a copy of s to the object that slave points to. This is likely not what you wanted to do. To reassign the pointer, do use a pointer (rather smart than raw ointer) and not a reference, which cannot be reassigned.
Helpful references:
For me, C++ for Java Programmers, especially the summary of differences between C++ and Java, has helped moving from Java to C++.
About cloning: Though assignment operators and regular ctors are usually used, the clone pattern is also helpful in C++. Good examples can be found in Item 25 of More Effective C++ (Virtualizing ctors and non-member functions).
Using VS 2015 with v120.
So I am getting memory exceptions because when I call make_shared() on an already constructed object. The already constructed object has a pointer to another object that was allocated with new so when it calls the destructor the first time. The object is destroyed, then when it calls it again, the object is already destroyed.
I thought that in this instance the object will be moved and the destructor is never called.
Code:
Sub-object:
#include "Obj.h"
Obj::Obj(int i)
{
cout << "const Obj " << this << "\n";
m_i = i;
}
Obj::~Obj()
{
cout << "-------------------DELETING o " << this << "\n";
}
Containing Object:
#include "BigObj.h"
BigObj::BigObj(int i)
{
cout << "const BIgObj " << this << "\n";
m_o = new Obj(i);
}
BigObj::BigObj()
{
}
BigObj::BigObj(const BigObj& o)
{
cout << "called copy const \n";
m_o = o.m_o;
}
BigObj::~BigObj()
{
delete m_o;
cout << "----------------DEL bigobj " << this << "\n";
}
Main:
int main(char argc, char** argv){
BigObj oo = BigObj(10);
shared_ptr<BigObj> shr= make_shared<BigObj>(oo);
BigObj oo2 = BigObj(100);
cout << "finished\n";
system("pause");
}
Output:
const BIgObj 000000C61F4FFAD8
const Obj 000000C61F658330
called copy const
const BIgObj 000000C61F4FFB28
const Obj 000000C61F658510
finished
Press any key to continue . . .
-------------------DELETING o 000000C61F658510
----------------DEL bigobj 000000C61F4FFB28
-------------------DELETING o 000000C61F658330
----------------DEL bigobj 000000C61F65AFB0
-------------------DELETING o 000000C61F658330
This is test code I made to show the problem. In the real project I create objects that need to contain a lot of info and then they are pushed onto a vector that is made shared for various threads to work on the info. This so whatever thread has the object last deletes it.
Erm - you copy constructor (of BigObj) copies the internal pointer (Obj) and then then there are two deletes on it (once the automatic instance is cleaned up and then the shared pointer) This is not good.. Copy constructor (of BigObj) is not doing the right thing, it should instantiate the subobject (Obj) using it's (of Obj) copy constructor...
EDIT: A clean-ish implementation..
class Obj
{
};
class BigObj
{
public:
// Default constructor
BigObj() : _obj(new Obj)
{ }
// Move constructor
BigObj(BigObj&& other) : _obj(move(other._obj)) // take ownership of the subobject
{ }
// Move assignment
BigObj& operator=(BigObj&& other)
{
_obj = move(other._obj); // take ownership of the subobject
return *this;
}
private:
unique_ptr<Obj> _obj;
};
This:
BigObj oo = BigObj(10);
shared_ptr<BigObj> shr= make_shared<BigObj>(oo);
Isn't doing what you think it is. You are creating a shared pointer and putting a copy of oo into it. Your copy constructor is copying m_o. This now means in your main you have TWO BigObj objects: oo and the newly made copy held by shr
Since both of their m_o members point at the same memory, their destructors will call delete on m_o twice.
You don't need oo first, you may construct the object in place using make_shared:
shared_ptr<BigObj> shr = make_shared<BigObj>(10);
This should fix your problem, but you should also look into deep versus shallow copies for why the copy constructor is problematic.
I have a few lines of code and I don't get, why and where the copy constructor is called. Could you explain it to me?
The output is:
CS10
CS99
CC100
Obj10=Obj100
D100
Obj10=Obj99
D99
D10
This is my source code:
#include <iostream>
using namespace std;
class my
{
int m;
public:
my(int i): m(i)
{
cout << "CS" << m << endl;
}
my(const my& c): m(c.m+1)
{
cout << "CC" << m << endl;
}
~my()
{
cout << "D" << m << endl;
}
my& operator=(const my &c)
{
cout << "Obj" << m << "=Obj" << c.m << endl;
return *this;
}
};
my f(my* x)
{
return *x;
}
int main()
{
my m1(10);
my m2(99);
m1 = f(&m2); // creates a new object
m1 = m2; // does not create a new object
}
Why and where is copy constructor called causing the output CC100 and D100?
In this function
my f(my* x)
{
return *x;
}
called in statement
m1 = f(&m2); // creates a new object
the copy constructor is called to copy object *x in the return temporary object.
In fact it looks as
my tmp = *x; // the copy constructor is called
m1 = tmp;
When trying to think about when a copy constructor is called you should keep a few things in mind:
Scope - functions can't see outside of themselves and their associated namespace. If you want to pass a variable to a function you need to save it in the global environment, repush a scoped copy and then operate on it.
When you use passing by reference you operate on the global copy but since in this case you are returning the value that is pointed to and not a pointer you have to push that return value onto the stack separately because it is stored at a different temporary register address that is popped off the stack after you assign it to a permanent location in main. That's where the destructor comes in.
You made a temporary return value to pass the value out of your function so it's got to be deleted because L1, L2, and L3 cache are all prime real estate.
I highly recommend doing a little bit of reading on assembly code operations or even try compiling simple programs into assembly and seeing how the low level languages work under the hood. Cheers!
I'm having trouble understanding why this code works. I've been in the C# world for awhile and wanted to brush up on C/C++ before diving into the new stuff in C++11 like RValue Refs and move semantics.
I'm wondering why this code that I wrote works:
class InnerMember
{
private:
int _iValue;
public:
InnerMember(): _iValue(0) {};
InnerMember(int iValue) : _iValue (iValue) {};
int GetValue(void) { return _iValue; }
int SetValue(int iValue) { _iValue = iValue; }
};
class TestClass
{
private:
InnerMember _objValue;
public:
TestClass() : _objValue(1) {};
void SetValue(int iValue)
{
_objValue.SetValue(iValue);
}
InnerMember& GetRef(void)
{
return _objValue;
}
virtual ~TestClass() { std::cout << "I've been released!" << std::endl; }
};
int main (int argc, char *argv[])
{
TestClass* pobjTest = new TestClass();
std::cout << "Before:" << std::endl;
std::cout << pobjTest->GetRef().GetValue() << std::endl;
pobjTest->SetValue(5);
InnerMember& robjInner = pobjTest->GetRef();
delete pobjTest;
std::cout << "After:" << std::endl;
std::cout << robjInner.GetValue();
return 0;
}
The output is:
Before:
1
I've been released!
After:
5
Press any key to continue...
I thought that this would cause an error, since I access the referenced object InnerMember from TestClass after TestClass has been destroyed. Is there some sort of return value optimization going on? Or is it really returning a copy instead of passing back the reference?
I used GCC to with no optimizations (-O0) and it still ran without an issue.
I also used the -S switch to generate the assembly but my AMD64 knowledge is rusty and the name mangling didn't help.
That is undefined behaviour, which means even the "correct" behaviour could happen. When you delete something in C++, it is not erased from the memory, so accessing it before something else writes over it will sometimes maybe still work.
robjInner is still a reference to some deleted object in memory.
This would lead to undefined behaviour.
After deletion, the reference robjInner has been left dangling. You get back the previous value because no one else claimed that piece of memory yet.
Copied from here
A previously-valid reference only becomes invalid in two cases:
If it refers to an object with automatic allocation which goes out of scope,
If it refers to an object inside a block of dynamic memory which has been freed.
The first is easy to detect automatically if the reference has static scoping, but is still a problem if the reference is a member of a dynamically allocated object; the second is more difficult to assure. These are the only concern with references, and are suitably addressed by a reasonable allocation policy.
You can add a print statement inside InnerMember destructor to see what is going on. You will see InnerMember is destroyed after TestClass and the 5 you get is because no one write to that part of memory yet. But that reference is not valid anymore.
Can someone tell why test(2) object is destroyed after test_method() call?
#include<iostream>
#include<string>
using namespace std;
class test
{
int n;
public:
test(int n) : n(n)
{
cout << "test: " << n << endl;
}
~test()
{
cout << "~test: " << n << endl;
}
test & test_method()
{
cout << "test_method: " << n << endl;
return *this;
}
};
int main(int argc, const char *argv[])
{
cout << "main start" << endl;
const test &test1 = test(1);
const test &test2 = test(2).test_method();
cout << "main end" << endl;
}
Output is:
main start
test: 1
test: 2
test_method: 2
~test: 2
main end
~test: 1
test(2).test_method() returns a reference, which is bound to test2, and then the object to which it refers is destroyed at the end of the full expression, since it is a temporary object. That should not be a surprise.
The real surprise is that test1 remains a valid reference, because it is directly bound to a temporary, and binding a temporary to a reference extends the lifetime of the temporary to that of the reference variable.
You only have to note that in the test(2) case, the temporary object isn't bound to anything. It's just used to invoke some member function, and then its job is done. It doesn't "babysit" member functions, or in other words, lifetime extension isn't transitive through all possible future references.
Here's a simple thought experiment why it would be impossible to actually have "arbitrary lifetime extension":
extern T & get_ref(T &);
{
T const & x = get_ref(T());
// stuff
// Is x still valid?
}
We have no idea if x remains valid beyond the first line. get_ref could be doing anything. If it's implemented as T & get_ref(T & x) { return x; }, we might hope for magic, but it could also be this:
namespace { T global; }
T & get_ref(T & unused) { return global; }
It's impossible to decide within the original translation unit whether anything needs to be extended or not. So the way the standard has it at present is that it's an entirely trivial, local decision, just made when looking at the reference declaration expression, what the lifetime of the temporary object in question should be.
Because the C++ standard requires this behavior. Give the object a name if you want it to persist. It will persist as long as the name.
Edit: You your example, test1 is the name that you gave to the first object, whereas the second object has obtained no name at all, and so it does not outlast evaluation of the expression.