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.
Related
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.
#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.
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 a Foo object, and a std::list holding instances of it. My problem is that when I add a new instance to the list, it first calls the ctor but then also the dtor. And then the dtor on another instance (according to the this pointer).
A single instance is added to the list but since its dtor (along with its parents) is called, the object cant be used as expected.
Heres some simplified code to illustrate the problem:
#include <iostream>
#include <list>
class Foo
{
public:
Foo()
{
int breakpoint = 0;
}
~Foo()
{
int breakpoint = 0;
}
};
int main()
{
std::list<Foo> li;
li.push_back(Foo());
}
When you push_back() your Foo object, the object is copied to the list's internal data structures, therefore the Dtor and the Ctor of another instance are called.
All standard STL container types in C++ take their items by value, therefore copying them as needed. For example, whenever a vector needs to grow, it is possible that all values in the vector get copied.
Maybe you want to store pointers instead of objects in the list. By doing that, only the pointers get copied instead of the object. But, by doing so, you have to make sure to delete the objects once you are done:
for (std::list<Foo*>::iterator it = list.begin(); it != list.end(); ++it) {
delete *it;
}
list.clear();
Alternatively, you can try to use some kind of 'smart pointer' class, for example from the Boost libraries.
You are creating a temporary Foo here:
li.push_back( Foo() )
push_back copies that Foo into its internal data structures. The temporary Foo is destroyed after push_back has been executed, which will call the destructor.
You will need a proper copy constructor that increases some reference count on the class members that you do not want to destroy early -- or make it private to force yourself on the pointer solution.
Use this object to understand:
class Foo
{
public:
Foo(int x): m_x(x)
{
std::cout << "Constructed Object: " << m_x << ")\n";
}
Foo(Foo const& c): m_x(c.m_x+100)
{
std::cout << "Copied Object: " << m_x << ")\n";
}
~Foo()
{
std::cout << "Destroyed Object: " << m_x << ")\n";
}
};
The First main
std::list<Foo*> li;
li.push_back(Foo(1));
Here we create a temporary Foo object and call push_back(). The temporary object gets copied into the list and the function returns. On completion of this statement the temporary object is then destroyed (via the destructor). When the list is destroyed it will also destroy all the obejcts it contains (Foo is an object with a destructor so destruction includes calling the destructor).
So you should see somthing like this:
Constructed Object: 1
Constructed Object: 101
DestroyedObject: 1
DestroyedObject: 101
In the second example you have:
std::list<Foo*> li;
li.push_back(new Foo(1));
Here you dynamically create an object on the heap. Then call the push_back(). Here the pointer is copied into the list (the pointer has no constructor/destructor) so nothing else happens. The list now contains a pointer to the object on the heap. When the function returns nothing else is done. When the list is destroyed it destroys (note the subtle difference betweens destroy and delete) the object it contains (a pointer) but a pointer has no destructor so nothing happens any you will leak memory.
So you should see somthing like this:
Constructed Object: 1
What actually happens here is that you store a copy of the passed object in the list, because you're sending it by value instead of by reference. So the first dtor that is called is actually called on the object you pass to the push_back method, but a new instance had been created by then and it is now stored in the list.
If you don't want a copy of the Foo object to be created, store pointers to Foo objects in the list instead of the objects themselves. Of course when doing it you will have to properly release memory on destruction of the list.
Making the list holding pointers instead of instances solves the problem with the destructor being called. But I still want to understand why it happens.
#include <iostream>
#include <list>
class Foo
{
public:
Foo()
{
int breakpoint = 0;
}
~Foo()
{
int breakpoint = 0;
}
};
int main()
{
std::list<Foo*> li;
li.push_back(new Foo());
}