#include <iostream>
#include <memory>
class Base
{
public:
virtual void foo() = 0;
};
class Derived : public Base
{
public:
void foo() override { std::cout << "Derived" << std::endl; }
};
class Concrete
{
public:
void Bar() { std::cout << "concrete" << std::endl; }
};
int main()
{
std::unique_ptr<Concrete> ConcretePtr = nullptr;
ConcretePtr->Bar();
std::unique_ptr<Base> BasePtr;
BasePtr->foo();
return 0;
}
I assume declaring a unique_ptr to a concrete type Concrete, allocates memory for an object of type Concrete and the unique_ptr starts pointing to it. Is my assumption/understanding correct ? I ask because ConcretePtr->Bar(); prints "concrete" to the console. But, if I make a unique pointer to an interface Base, it does not know the exact type of object that I need and does not allocate/acquire resources in the memory.
This fails at BasePtr->foo(); with BasePtr._Mypair._Myval2 was nullptr.
Why does the first declaration std::unique_ptr<Concrete> ConcretePtr = nullptr; allocate an object by itself ? what if I did not want it to be pointing to some real object at that very line of code, but wanted only a smart pointer ?
Now if I change the declaration to be std::unique_ptr<Concrete> ConcretePtr; and the Concrete type to be the following,
class Concrete
{
int ConcreteNum;
public:
void Bar()
{
std::cout << "concrete" << std::endl;
ConcreteNum = 38;
std::cout << ConcreteNum << std::endl;
}
};
it fails at ConcreteNum = 38; complaining that this was nullptr; if this this was nullptr then why and how did the earlier call (where Concrete did not have any state ConcreteNum) to Bar work ?
Moreover why does it not fail at ConcretePtr->Bar(); (this -> requires a concrete object, does it not ? what was this here ?) but inside Bar, in that assignment ?
I see the same issue with std::shared_ptr as well. I'm not very sure of the difference between declaration, initialization & assignment. Please help me understand.
I'm using MSVC.
The unique_ptr models a pointer. That is, it's an object that points to another object.
Initialising the unique_ptr with nullptr creates it in the state where it is not pointing to or owning another object.
It's like saying Concrete* p = nullptr.
Initialise it in one of these ways:
std::unique_ptr<Concrete> p{new Concrete()};
or
std::unique_ptr<Concrete> p; // = nullptr is implied.
p.reset(new Concrete());
or, better:
std::unique_ptr<Concrete> p = std::make_unique<Concrete>();
or simply:
auto p = std::make_unique<Concrete>();
But be careful in this case if you really want to be pointing to the Base interface:
std::unique_ptr<Base> p = std::make_unique<Derived>();
or
std::unique_ptr<Base> p = nullptr;
p = std::make_unique<Derived>(); // assignment from rvalue ref of compatible unique_ptr.
std::unique_ptr<Concrete> ConcretePtr = nullptr;
I assume declaring a unique_ptr to a concrete type Concrete, allocates memory for an object of type Concrete and the unique_ptr starts pointing to it. Is my assumption/understanding correct ?
Well, you can trivially check. Write a default constructor for Concrete that prints something out so you can tell when an instance is created. Run the smallest possible program (just the line above in main). Did you see the expected output?
You should be checking this stuff before asking a question (and probably after reading the documentation), but to save you time: no, that line doesn't construct an object of type Concrete.
You can also check explicitly whether unique_ptr is managing an object, with
if (!ConcretePtr) {
std::cout << "ConcretePtr doesn't point to anything\n";
} else {
std::cout << "ConcretePtr owns an object\n";
}
This check is also trivial, and you could easily do it before asking a question.
I ask because ConcretePtr->Bar(); prints "concrete" to the console
This is a bad test because if the pointer is a nullptr, it's undefined behaviour. If you care whether the pointer is a nullptr, you should check that explicitly before dereferencing it, as above.
To demonstrate why this test is confusing you (and you should use the ones above in preference), consider a likely implementation of non-virtual member functions (recall they get an implicit this pointer):
// void Concrete::Bar() implemented as
void Concrete_Bar(Concrete *this)
// and ConcretePtr->Bar() implemented as
Concrete_Bar(ConcretePtr.get());
so, you just passed a nullptr to a function that ignores its only parameter, and you never tested the thing you thought you did.
Related
I would like to use an object that has specific deleter and constructor in a smart pointer.
The object is called: TF_Status - it is created with the function TF_NewStatus() and delete with the function TF_DeleteStatus().
When I want to use it with a smart pointer, I will initialize it like this:
std::unique_ptr<TF_Status, void(*)(TF_Status*)> status(TF_NewStatus(), TF_DeleteStatus);
However, when I want to use it as a class member, I do not want to create the object in the c'tor, because I have to set some values before. The problem is that there is no default c'tor for this kind of smart pointer.
Do you guys have any solutions?
When I understand correctly, you need something like this:
struct some_t
{
some_t()
: m_status(nullptr, TF_DeleteStatus)
{}
void later()
{
m_status.reset(TF_NewStatus());
}
std::unique_ptr<TF_Status, void(*)(TF_Status*)> m_status;
};
The unique_ptr becomes a zero pointer at construction time. Later you can create the TF_Status object.
You can instantiate a unique_ptr holding a null pointer first, and then reset this pointer with an actual TF_Status:
#include <iostream>
// TF_*
struct TF_Status {};
TF_Status* TF_NewStatus() { std::cout << "\tNew TF_Status\n" ; return new TF_Status(); }
void TF_DeleteStatus(TF_Status* s) { std::cout << "\tDelete TF_Status\n"; delete s; }
#include <memory>
class C
{
std::unique_ptr<TF_Status, decltype(TF_DeleteStatus)*> status;
public:
C() : status(nullptr, TF_DeleteStatus) {}
void acquire_status() {
status.reset(TF_NewStatus());
}
};
// test
int main()
{
std::cout << "About to instantiate a C\n";
C c;
std::cout << "done\n";
std::cout << "About to acquire_status\n";
c.acquire_status();
std::cout << "done\n";
}
Output
g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
About to instantiate a C
done
About to acquire_status
New TF_Status
done
Delete TF_Status
Note that std::unique_ptr::reset :
Given current_ptr, the pointer that was managed by *this, performs the following actions, in this order:
Saves a copy of the current pointer old_ptr = current_ptr
Overwrites the current pointer with the argument current_ptr = ptr
If the old pointer was non-empty, deletes the previously managed object if(old_ptr) get_deleter()(old_ptr).
So you're safe even if your smart pointer contains an actual TF_Status when you invoke reset() on it.
(demo)
I suggest creating a deleter class:
struct TF_Status;
void TF_DeleteStatus(TF_Status*);
struct TF_StatusDeleter {
void operator()(TF_Status* s) noexcept { TF_DeleteStatus(s); }
};
And then:
using TF_StatusPtr = std::unique_ptr<TF_Status, TF_StatusDeleter>;
This way TF_StatusPtr can be default-initialized (no need to explicitly initialize this member in the constructor) and sizeof(TF_StatusPtr) == sizeof(TF_Status*). std::unique_ptr uses empty base-class optimization for the deleter and hence a TF_StatusDeleter sub-object with no data members occupies 0 bytes.
Whereas std::unique_ptr<TF_Status, void(*)(TF_Status*)> is twice as big because each instance stores a pointer to same TF_DeleteStatus function, which is a waste.
I have just started using std::variant in my projects. I have a doubt. What will the destructor of std::variant do in the code shown below. Variant holds a void* data. Once variant goes out of scope, I think it will only free the memory of void* but not the actual object the pointer was pointing to. So there will be memory leak in this case. I would like to know if my understanding is correct or not.
#include <iostream>
#include <memory>
#include <variant>
using namespace std;
class A {
public:
~A(){
cout<<"Destructor called"<<endl;
}
};
int main() {
std::variant<void*> data;
A* b = new A();
data = (void*)b;
return 0;
}
When the variant destructor fires, it will call the destructor for whatever type of item is stored in the variant at that point. If that’s a void*, then C++ will say “okay, I will clean up the void*, and since that’s a primitive type, that’s a no-op.” It won’t look at the void*, realize that it’s actually a pointer to an A, and then delete the pointer as though it’s an A*.
The comments have pointed out that it’s fairly unusual to use a variant of a void*. A void* means “I’m pointing at something, and it’s up to you as the user to keep track of what it is and do the appropriate casting and resource management.” A variant means “I’m holding one of the following actual things, and I want C++ to remember which one and to do the appropriate resource management for me.” You may want to rethink your design, as there might be an easier way to do whatever you’re aiming to do here.
You are correct. The only pointer owning classes in the standard library that actually does delete (or delete[]) on pointers are the smart pointers.
std::variant is supposed to support you to hold one object of any number of types and primarily not pointers to objects. If the variant contains pointers, it means that some other object owns the data and is responsible for deleting it.
A std::variant capable of holding only one type is rarely useful either. You can declare the variable as a normal variable of that type in that case.
Here's one example of using a std::variant capable of holding objects of two unrelated types, and destruction will happen as expected.
#include <iostream>
#include <variant>
class A {
public:
~A() { std::cout << "A destructor called\n"; }
};
class B {
public:
B() {}
B(const B&) = default;
B& operator=(const B&) = default;
~B() { std::cout << "B destructor called\n"; }
};
int main() {
std::variant<A, B> data; // now holds a default constructed A
data = B(); // deletes the A and now holds a default constructed B
std::cout << "---\n";
}
Output:
A destructor called // in "data = B()", the A must be destroyed
B destructor called // the temporary B used in "data = B()"
---
B destructor called // the B in the variant when the variant goes out of scope
I tried looking for an answer, but couldn't find anything helpful to understand what happens behind the scenes.
Assuming that we have the following:
class Base
{
public:
virtual int request() = 0;
void* operator new(long unsigned int size) = delete;
};
class Derived final : public Base
{
public:
int request() override;
};
int Derived::request()
{
return 2;
}
On the base class I have deleted the new operator because I don't want to be able to create pointers.
I know it might not make any sense, it is just an experiment.
When I call g++ gives me a compilation error to tell me that the new operator is deleted. Exactly what I expected.
int main()
{
auto d = std::make_unique<Derived>();
int value = d->request();
std::cout << "Request value is " << value << "." << std::endl;
return 0;
}
But if I create a shared_ptr the code compiles and runs fine.
auto d = std::make_shared<Derived>();
Why is this code valid when creating shared pointers. The operator is not being used in this case?
std::make_shared use global placement new version of the operator new, by std::allocator::construct
It is necessary to store object and atomic reference counter in the same block of memory, so that shared_ptr template works almost equal to intrusive smart pointer.
You can not prevent std::make_shared to construct a smart pointer.
std::shared_ptr has a nifty templated constructor that automagically creates the right deleter for its given type (constructor #2 in that link).
Until just now, I (erroneously) thought std::unique_ptr had a similar constructor, but when I ran the following code:
#include <memory>
#include <iostream>
// Notice nothing is virtual
struct Foo
{
~Foo() { std::cout << "Foo\n"; }
};
struct Bar : public Foo
{
~Bar() { std::cout << "Bar\n"; }
};
int main()
{
{
std::cout << "shared_ptr:\n";
std::shared_ptr<Foo> p(new Bar()); // prints Bar Foo
}
{
std::cout << "unique_ptr:\n";
std::unique_ptr<Foo> p(new Bar()); // prints Foo
}
}
I was surprised to learn that unique_ptr doesn't call Bar's destructor.
What's a clean, simple, and correct way to create a unique_ptr that has the correct deleter for its given pointer? Especially if I want to store a whole list of these (i.e. std::vector<std::unique_ptr<Foo>>), which means that they all must have a heterogeneous type?
(pardon the poor title; feel free to suggest a better one)
You should make the destructor of Foo virtual. That is good practice regardless of whether you use unique_ptr or not. That will also take care the problem that you are dealing with.
Here's one way:
{
std::cout << "unique_ptr<Bar, void(void*)>:\n";
std::unique_ptr<Foo, void(*)(void*)> p(
new Bar(), [](void*p) -> void { delete static_cast<Bar*>( p ); }
); // prints Bar Foo
}
A main problem with this approach is that unique_ptr supports conversion to logical "pointer to base class", but that the standard does not guarantee that conversion to void* will then yield the same address. In practice that's only a problem if the base class is non-polymorphic while the derived class is polymorphic, introducing a vtable ptr and thus possibly changing the memory layout a bit. But in that possible-but-not-likely situation the cast back in the deleter would yield an incorrect pointer value, and bang.
So, the above is not formally safe with respect to such conversions.
To do roughly the same as a shared_ptr does (shared_ptr supports conversions to logical pointer-to-base), you would need to store also the original void* pointer, along with the deleter.
In general, when you control the the topmost base class, make its destructor virtual.
That takes care of everything.
I need to store references to instances of derived classes in C++. I considered using a vector of shared_ptrs to the base class (for it needs to hold different types of derived classes), however, it's important that the container holds the original pointers, which is not the case with vectors (or other stl containers), if I'm not mistaken. Is there a way to do this in native C++, or do I have to use special containers like Boost's ptr_vector?
EDIT: This is my test code:
class Foo
{
public:
Foo() {}
virtual ~Foo() {}
virtual void set_x(int i) = 0;
};
class Bar : public Foo
{
public:
Bar() {}
void set_x(int i)
{
this->x = i;
}
int x;
};
int main()
{
Bar bar;
// ptr
std::cout << &bar << "\n";
std::vector<Foo*> foos;
foos.push_back(&bar);
// different ptr value
std::cout << &foos[0] << "\n";
foos[0]->set_x(1);
// however, changes are made
std::cout << bar.x;
return 0;
}
Thanks in advance,
jena
In your example above, what you are printing out is the address of the pointer not the value of the pointer.
Instead of:
// different ptr value
std::cout << &foos[0] << "\n";
Do
// different ptr value
std::cout << foos[0] << "\n";
Aside from that your vector<Foo*> will work just fine.
You can create a std::vector<foo*>, which will hold any pointers to foo that you hand to it. It won't make any attempt to delete those pointers on destruction, which may or may not be what you want, but it will hold exactly the values you pass in.
You can also create an std::vector< shared_ptr<foo> >, which will hold pointers that will be released once there are no dangling copies of the shared_ptr floating around. Those will also hold the "original" foo* you passed in; you can get it again by using the shared_ptr::get() method.
The only time you wouldn't see exactly the same pointer as your derived object is if you're using multiple inheritance of classes, and your base classes include data. Because a foo* would end up, in that case, pointing to the "foo" part of the data, which wouldn't necessarily be at the "root" of the object.
If you use shared_ptr as your container member, the pointer in each member will retain access to the original object instance. You can get a copy of a shared_ptr at any point after container housekeeping, but the original object will still be its target.
For a simpler solution you might use boost::ptr_vector, provided none of your pointers occur twice in the container - that scenario would introduce tricky resource management complexity and point you back to shared_ptr.