As far as I know, auto_ptr works on the concept of transfer of ownership. Also, once an auto pointer transfer its ownership to another auto pointer, it should not be able refer to the object it points to anymore. However, this is not the case I found as shown in the below program. Am I missing something? Please help.
#include <iostream>
#include <string>
#include <memory>
class A
{
public:
void display(){
std::cout << "Inside class A" << std::endl;
}
};
int main()
{
std::auto_ptr<A> p1(new A());
std::auto_ptr<A> p2;
std::cout << p1.get() << std::endl;
p2=p1;
std::cout << p2.get() << std::endl;
p2->display();
std::cout <<p1.get()<< std::endl; //Address of shows 0 as expected
p1->display(); //L1
std::auto_ptr<A> p3 = p1; //L2
std::cout << p3.get() << std::endl; //Address shows 0 as expected
p3->display();
return 0;
}
Output:
0x45a0620
0x45a0620
Inside class A
0
Inside class A
0
Inside class A
Line L1: How does this work, since p1 do not have the ownership anymore?
Line L2: How does this work, since p1 do not have the ownership anymore?
Your code doesn't show what you think it does.
This is straightforward Undefined Behaviour: the auto_pointer here is only obscuring the fact that your code reduces to:
A *a {nullptr};
a->display();
Consider method A::display - it isn't virtual, so is essentially a simple function whose name has class scope, and which receives through some mechanism an implicit this pointer to the object on which it was invoked.
Since the function address doesn't depend on the object pointer, your compiled has emitted code which calls the function successfully, just with this==nullptr.
If you display prints the value of this, or use a non-static data member of A inside the function, this should be apparent.
Finally, as NathanOliver pointed out, auto_pointer is deprecated anyway and for good reason.
Related
I just wondering if the following way of delivering a pointer variable, created inside of the func1, to the caller (func2) is a correct way of doing this. If this is correct, will it release the memory when func2 is returned? If it is a bad idea, why is that?
int & func1()
{
std::shared_ptr<int> d = std::make_shared<int>(50);
return *d;
}
void func2(){
int & dd = func1();
}
This is a simplified code. I am assuming the size of d is huge(e.g images).
Added:
I realized that the following also works. What will be the pros and cons of each approach?
std::shared_ptr<int> & func1()
{
std::shared_ptr<int> d = std::make_shared<int>(50);
return d;
}
void func2()
{
std::shared_ptr<int> & dd = func1();
}
Both of those examples are bad. You can't use the return values of either func1, they are always dangling references.
int & func1()
{
std::shared_ptr<int> d = std::make_shared<int>(50);
return *d;
} // d is the only owner when it is destroyed, *d is also deleted
std::shared_ptr<int> & func1()
{
std::shared_ptr<int> d = std::make_shared<int>(50);
return d;
} // d is destroyed here
I am assuming the size of d is huge
You are mistaken. The size of the object pointed-to by d has no bearing on the size of d, just like with raw pointers.
E.g.
#include <iostream>
#include <memory>
struct Huge
{
int data[100000];
};
int main()
{
std::cout << sizeof(int) << std::endl
<< sizeof(int*) << std::endl
<< sizeof(std::shared_ptr<int>) << std::endl
<< sizeof(std::unique_ptr<int>) << std::endl
<< sizeof(Huge) << std::endl
<< sizeof(Huge*) << std::endl
<< sizeof(std::shared_ptr<Huge>) << std::endl
<< sizeof(std::unique_ptr<Huge>) << std::endl;
}
for me results in
4
8
16
8
400000
8
16
8
I realized that the following also works
If by works, you mean "is accepted by a C++ compiler", then yes. They both result in undefined behaviour if you use the references returned, so I would say they categorically don't work.
I wanted to make a simple comment out of this, but there was too much to be said. This is meant to be a precision of mathematician1975's comment, which in my opinion is a good point to bring up.
I thought just returning shared_ptr will require extra cost to copy the control block.
You may want to have a look at (N)RVO/copy elision, which is precisely the mechanism avoiding this kind of thing: https://en.cppreference.com/w/cpp/language/copy_elision.
Long story short: returning it won't copy it, but instead will build it in-place at caller's site. No performance cost!
I've made a basic live example which shows how (N)RVO works, available here: http://coliru.stacked-crooked.com/a/8a32afc3775c685e
EDIT: If it can help clarifying the process of all this, I've written an article about copy elision and [N]RVO.
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.
With regards to this piece of code:
#include <iostream>
class CClass1
{
public:
void print() {
std::cout << "This should print first" << std::endl;
}
};
class CClass2
{
public:
void print() {
std::cout << "This should print second" << std::endl;
}
};
So someone asked an interesting question about having a "free pointer" (so to speak) which can point to multiple instances of different objects without having to create a new type of that object. The person had the idea that this pointer can be of type void * and since it is void, it can be made to point to any instance of an object and access the object's public properties.
The following solution was submitted:
int main() {
void *pClass(NULL);
((CClass1 *)(pClass))->print();
((CClass2 *)(pClass))->print();
std::cin.ignore();
return 0;
}
My question is why does the above work, but this doesn't:
int main() {
(CClass1 *FG)->print();
(CClass2 *FG)->print();
std::cin.ignore();
return 0;
}
Your first example exhibits undefined behavior, by calling a non-static member function via a pointer that doesn't point to a valid object. It only appears to work by accident, because the function in question just happens not to use this in any way.
Your second example is, quite simply, syntactically incorrect. I'm not even sure what you are trying to do there; the code makes no sense.
I'm trying to implement smart pointers in my code. I've created a class to convert a Point to a shared_ptr and I've added a print function for the shared_ptr. In my main, I create an array of shared_ptr of type Shape. When I assign Points to the array, I only see raw constructors/destructors, rather than the shared constructor/destructors. Is this code correct?
Thanks.
#include "Point_H.hpp"
#include "Shape_H.hpp"
#include "Array_H.hpp"
#include "boost/shared_ptr.hpp"
using namespace CLARK::Containers;
using namespace CLARK::CAD;
class P1
{
private:
boost::shared_ptr<Point> pp;
public:
P1(boost::shared_ptr<Point> value) : pp(value) { cout << "P1 constructor call (default)" << endl; }
virtual ~P1() { cout << "P1 destructor call" << endl; }
void print() const { cout << "Point: " << *pp << endl; }
};
void Print()
{
boost::shared_ptr<Point> myPoint (new Point);
{
P1 point1(myPoint);
point1.print();
}
}
int main()
{
// Typedef for a shared pointer to shape
// a typedef for an array with shapes stored as shared pointers.
typedef boost::shared_ptr<Shape> ShapePtr;
typedef Array<ShapePtr> ShapeArray;
ShapeArray my_ShapeArray(3);
ShapePtr my_Point (new Point(3.1459, 3.1459));
my_ShapeArray[0] = my_Point;
my_ShapeArray[0]->Print();
return 0;
}
The output looks like the below (the constructor/destructor statements are from the Point/Shape/Array classes themselves, rather than from the code in this source file.
Array constructor call
Shape constructor call (default)
Point constructor call (3.1459,3.1459) ID:41
Point destructor call
Shape destructor call
Array destructor call
I was expecting to see shared_ptr constructor/destructor statements. Is my problem in the P1 code or in my implementation in the main or elsewhere?
Thanks!
You're calling
my_ShapeArray[0]->Print();
which must be a member function of Shape.
You are not calling the
Print();
function which you define in the code given and which is the one using the P1 class.
You aren't calling any single bit from your code in the main function... You never instantiate P1, you never call the standalone function Print, so how could any of your logging statement ever get called?
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.