I'm trying to make something like this work:
struct holder {
std::function<void()> destroyer;
template<typename T>
holder(T) = delete;
template<typename T>
holder(std::enable_if< WAS CREATED WITH new > pointer) {
destroyer = [=] { delete pointer; };
};
template<typename T>
holder(std::enable_if< WAS CREATED WITH new[] > array) {
destroyer = [=] { delete[] array; };
};
virtual ~holder() {
destroyer();
};
};
In a way that I could then simply make return new test; and return = new test[10]; on a function that would return holder. But I found out that it won't ever be treated as an array, as operator new[] returns a pointer.
Is there any way to achieve the desired result?
Thanks! :)
It is impossible; whether or not new or new[] was used is not part of the pointer's type information.
The only way I know of is through placement-new:
#include <new>
#include <iostream>
struct A
{
void* operator new(std::size_t n, void* ptr)
{
std::cout << "operator new()\n";
return ptr;
}
void* operator new[](std::size_t n, void* ptr)
{
std::cout << "operator new[]\n";
return ptr;
}
};
int main()
{
A* ptr;
new (ptr) A();
new (ptr) A[5];
}
Related
I am curious to know, how shared_ptr class decides whether it has to call delete or delete[] for the below statements?
a. boost::shared_ptr <char> ptr(new char);
b. boost::shared_ptr <char []> ptr(new char[100]);
It is done via template specialization. Here is the simple demonstration.
#include <iostream>
#include <type_traits>
template<typename T>
struct foo
{
foo( T* d ) : m_data{ d } { }
~foo() {
if ( m_data )
std::cout << "delete called." << std::endl;
}
private:
T* m_data {};
};
template<typename T>
struct foo<T[]>
{
foo( T* d ) : m_data { d } { }
~foo() {
if ( m_data )
delete[] m_data;
std::cout << "delete[] called." << std::endl;
}
private:
T* m_data {};
};
int main()
{
foo<char> inst_1 { new char };
foo<char []> inst_2 { new char[ 100 ] { 'a' } };
}
online example
For T[] types, struct foo<T[]> instantiated, so compile-time polymorphism is done via template specialization and smart_ptr handles deallocation of both array and normal types with the help of this feature.
I have heard people say that "C++ doesn't need placement delete because it wouldn't do anything."
Consider the following code:
#include <cstdlib>
#include <cstdio>
#include <new>
////////////////////////////////////////////////////////////////
template<typename T, typename... ARGS>
T* customNew1(ARGS&&... args) {
printf("customNew1...\n");
auto ret = new T { std::forward<ARGS>(args)... };
printf("OK\n\n");
return ret;
}
template<typename T>
void customDelete1(T *ptr) {
printf("customDelete1...\n");
delete ptr;
printf("OK\n\n");
}
////////////////////////////////
template<typename T, typename... ARGS>
T* customNew2(ARGS&&... args) {
printf("customNew2 alloc...\n");
void *buf = std::malloc(sizeof(T));
printf("customNew2 construct...\n");
auto ret = ::new(buf) T { std::forward<ARGS>(args)... };
printf("OK\n\n");
return ret;
}
template<typename T>
void customDelete2(T *ptr) {
printf("customDelete2 destruct...\n");
// what I want: a "placement delete" which calls the destructor and returns the address that should be passed to the deallocation function
// e.g.
//
// void* ptrToFree = ::delete(ptr);
// std::free(ptrToFree);
//
// equally fine would be a "magic" operator that allows one to obtain said address without actually calling the destructor:
//
// void* ptrToFree = get_deallocation_address_of(ptr);
// ptr->~T();
// std::free(ptrToFree);
ptr->~T();
printf("customDelete2 free...\n");
std::free(ptr);
printf("OK\n\n");
}
////////////////////////////////////////////////////////////////
struct A {
int a;
A() : a(0) {
printf("A()\n");
}
virtual ~A() {
printf("~A()\n");
}
};
struct B {
int b;
B() : b(0) {
printf("B()\n");
}
virtual ~B() {
printf("~B()\n");
}
};
struct C : A, B {
int c;
C() : c(0) {
printf("C()\n");
}
~C() {
printf("~C()\n");
}
};
////////////////////////////////////////////////////////////////
int main() {
C *c1 = customNew1<C>();
A *a1 = c1;
B *b1 = c1;
// Assume c and a will be the same but b is offset
printf("c: %x\n", c1);
printf("a: %x\n", a1);
printf("b: %x\n", b1);
printf("\n");
customDelete1(b1); // <- this will work, the delete expression offsets b1 before deallocing
printf("--------------\n\n");
C *c2 = customNew2<C>();
A *a2 = c2;
B *b2 = c2;
printf("c: %x\n", c2);
printf("a: %x\n", a2);
printf("b: %x\n", b2);
printf("\n");
// customDelete2(b2); // <- this will break
customDelete2(a2); // <- this will work because a2 happens to point at the same address as c2
printf("--------------\n\n");
return 0;
}
As you can see here the destructors, being virtual, are all called properly, but the deallocation of b2 will still fail because b2 points at a different address than c2.
Note that a similar problem arises when one uses placement new[] to construct an array of objects, as described here:
Global "placement" delete[]
However this can be worked around without much trouble by simply saving the array size at the head of your block of memory and handling the array constructor/destructor calls manually in a loop using single object placement new/explicit destructor calls.
On the other hand, I cannot think of any graceful way to solve the problem with multiple inheritance. The "magic" code which retrieves the original pointer from the base pointer within the delete expression is implementation specific, and there's no simple way of "doing it manually" like you can with arrays.
Here is another situation where this becomes a problem, with an ugly hack to work around it:
#include <cstdlib>
#include <cstdio>
#include <new>
////////////////////////////////////////////////////////////////
// imagine this is a library in which all allocations/deallocations must be handled by this base interface
class Alloc {
public:
virtual void* alloc(std::size_t sz) =0;
virtual void free(void *ptr) =0;
};
// here is version which uses the normal allocation functions
class NormalAlloc : public Alloc {
public:
void* alloc(std::size_t sz) override final {
return std::malloc(sz);
}
void free(void *ptr) override final {
std::free(ptr);
}
};
// imagine we have a bunch of other versions like this that use different allocation schemes/memory heaps/etc.
class SuperEfficientAlloc : public Alloc {
void* alloc(std::size_t sz) override final {
// some routine for allocating super efficient memory...
(void)sz;
return nullptr;
}
void free(void *ptr) override final {
// some routine for freeing super efficient memory...
(void)ptr;
}
};
// etc...
////////////////////////////////
// in this library we will never call new or delete, instead we will always use the below functions
// this is used instead of new...
template<typename T, typename... ARGS>
T* customNew(Alloc &alloc, ARGS&&... args) {
printf("customNew alloc...\n");
void *buf = alloc.alloc(sizeof(T));
printf("customNew construct...\n");
auto ret = ::new(buf) T { std::forward<ARGS>(args)... };
printf("OK\n\n");
return ret;
}
// um...
thread_local Alloc *stupidHack = nullptr;
// unfortunately we also have to replace the global delete in order for this hack to work
void operator delete(void *ptr) {
if (stupidHack) {
// the ptr that gets passed here is pointing at the right spot thanks to the delete expression below
// alloc has been stored in "stupidHack" since it can't be passed as an argument...
printf("customDelete free # %x...\n", ptr);
stupidHack->free(ptr);
stupidHack = nullptr;
} else {
// well fug :-D
}
}
// ...and this is used instead of delete
template<typename T>
void customDelete(Alloc &alloc, T *ptr) {
printf("customDelete destruct # %x...\n", ptr);
// set this here so we can use it in operator delete above
stupidHack = &alloc;
// this calls the destructor and offsets the pointer to the right spot to be dealloc'd
delete ptr;
printf("OK\n\n");
}
////////////////////////////////////////////////////////////////
struct A {
int a;
A() : a(0) {
printf("A()\n");
}
virtual ~A() {
printf("~A()\n");
}
};
struct B {
int b;
B() : b(0) {
printf("B()\n");
}
virtual ~B() {
printf("~B()\n");
}
};
struct C : A, B {
int c;
C() : c(0) {
printf("C()\n");
}
~C() {
printf("~C()\n");
}
};
////////////////////////////////////////////////////////////////
int main() {
NormalAlloc alloc;
C *c = customNew<C>(alloc);
A *a = c;
B *b = c;
printf("c: %x\n", c);
printf("a: %x\n", a);
printf("b: %x\n", b);
printf("\n");
// now it works
customDelete(alloc, b);
printf("--------------\n\n");
return 0;
}
This isn't a question really more of just a rant as I'm fairly sure that no magic operator or platform independent method to obtain the address exists. At the company where I work we had a library that used custom allocators with the hack above which worked okay until we had to link it statically with another program that needed to replace global new/delete. Our current solution is simply to ban the deleting of an object through a pointer to a base that can't be shown to always have the same address as the most derived object, but this seems a bit unfortunate. "ptr->~T(); free(ptr);" seems to be a common enough pattern and many people seem to think it's equivalent to a delete expression, but it's not. I'm curious if anyone else has encountered this problem and how they managed to solve it.
If p points to an object of polymorphic class type, you can get the address of the most derived object using dynamic_cast<void*>(p). Thus your customDelete2 can be implemented as follows:
template <class T>
void customDelete2(const T *ptr) {
const void* ptr_to_free = dynamic_cast<const void*>(ptr);
ptr->~T();
std::free(const_cast<void*>(ptr_to_free));
}
(Yes, you can dynamically allocate const objects.)
Since this will only compile for a polymorphic class type, you might want to remove the dynamic_cast to a helper function:
template <class T>
const void* get_complete_object_address(const T* p, std::true_type) {
return dynamic_cast<const void*>(p);
}
template <class T>
const void* get_complete_object_address(const T* p, std::false_type) {
return p;
}
template <class T>
void customDelete2(const T *ptr) {
const void* ptr_to_free = get_complete_object_address(
ptr,
std::integral_constant<bool, std::is_polymorphic<T>::value>{}
);
ptr->~T();
free(const_cast<void*>(ptr_to_free));
}
I have a class X and method addX() which allocates objects of X on the heap. I want to restrict client-code from directly allocating objects of X (so that the X *ptr = new X is not allowed).
I've declared new, new[] operators private, but since I'm allocating X's objects through addX() I need to define them (operators). So, what's their definition should look like?
Hide the constructors, use a factory function:
class A
{
public:
static A* create() { return new A; }
static void destroy(A* a) { delete a; }
protected:
A() {}
A(const A&) {}
A& operator=(const A&) {}
};
This is pretty easy.
#include <iostream>
#include <new>
class X {
public:
static void addX()
{
X* p1 = new X;
delete p1;
}
private:
void* operator new(std::size_t sz)
{
std::cout << "custom new for size " << sz << '\n';
return ::operator new(sz);
}
void* operator new[](std::size_t sz)
{
return ::operator new(sz);
}
};
int main()
{
X::addX();
}
Why if I overload the -> operator in this code
class subobj
{
public:
void get()
{
printf("ea");
}
};
template<typename T> class testPT
{
public:
T* operator->()
{
return ptr;
}
T* ptr;
};
int main()
{
subobj myobj;
testPT<subobj> myclass;
myclass.ptr = &myobj;
myclass->get();
return 0;
}
I get the "ea" string printed?
By using "myclass->", that should just return a T*, a pointer to the object. I should have done something like
myclass->->get()
to actually call the get() routine. Where am I getting wrong?
operator-> is magic. :)
It uses chaining, that means it is called again as long as you don't return a plain pointer. When you return a plain pointer, it does one final call to operator->. When you call operator->
obj->foo;
it translates to:
(obj.operator->())->foo;
except when obj is a plain pointer.
You could even do this:
template<typename T> class testPT2
{
public:
T* operator->()
{
return ptr;
}
T* ptr;
};
template<typename T> class testPT
{
public:
testPT2<T> operator->()
{
testPT2<T> p2;
p2.ptr = ptr;
return p2;
}
T* ptr;
};
and it would still work by effectively applying operator-> three times.
Consider the following piece of code:
int three() {
return 3;
}
template <typename T>
class Foo {
private:
T* ptr;
public:
void bar(T& t) { ptr = new T(t); }
void bar(const T& t) { ptr = new T(t); }
void bar(T&& t) { (*ptr) = t; } // <--- Unsafe!
};
int main() {
Foo<int> foo;
int a = 3;
const int b = 3;
foo.bar(a); // <--- Calls Foo::bar(T& t)
foo.bar(b); // <--- Calls Foo::bar(const T& t)
foo.bar(three()); // <--- Calls Foo::bar(T&& t); Runs fine, but only if either of the other two are called first!
return 0;
}
My question is, why does the third overload Foo::bar(T&& t) crash the program? What exactly is happening here? Does the parameter t get destroyed after the function returns?
Furthermore, let's assume that the template parameter T was a very large object with a very costly copy constructor. Is there any way to use RValue References to assign it to Foo::ptr without directly accessing this pointer and making a copy?
In this line
void bar(T&& t) { (*ptr) = t; } // <--- Unsafe!
you can dereference an uninitialized pointer. This is undefined behavior.
You must call one of the two other version of bar first because you need to create the memory for your object.
So I would do ptr = new T(std::move(t));.
If your type T supports moving the move constructor will get called.
Update
I would suggest something like that. Not sure if you need the pointer type within foo:
template <typename T>
class Foo {
private:
T obj;
public:
void bar(T& t) { obj = t; } // assignment
void bar(const T& t) { obj = t; } // assignment
void bar(T&& t) { obj = std::move(t); } // move assign
};
This would avoid memory leaks which are also quite easy with your approach.
If you really need the pointer in your class foo how about that:
template <typename T>
class Foo {
private:
T* ptr;
public:
Foo():ptr(nullptr){}
~Foo(){delete ptr;}
void bar(T& t) {
if(ptr)
(*ptr) = t;
else
ptr = new T(t);
}
void bar(const T& t) {
if(ptr)
(*ptr) = t;
else
ptr = new T(t);
}
void bar(T&& t) {
if(ptr)
(*ptr) = std::move(t);
else
ptr = new T(std::move(t));
}
};
There's no reason for that to fail in that code. ptr will point to an existing int object created by the previous calls to bar and the third overload will just assign the new value to that object.
However, if you did this instead:
int main() {
Foo<int> foo;
int a = 3;
const int b = 3;
foo.bar(three()); // <--- UB
return 0;
}
That foo.bar(three()); line would have undefined behaviour (which does not imply any exception), because ptr would not be a valid pointer to an int object.
Assuming that you only called foo.bar(three()); without the other two calls:
Why did you think that'd work? Your code is essentially equivalent to this:
int * p;
*p = 3;
That's undefined behaviour, because p isn't pointing to a valid variable of type int.
The "unsafe"thing, here is that, before assigning to ptr a new object, you should worry about the destiny of what ptr actually points to.
foo.bar(three());
is unsafe in the sense that you have to grant -before calling it- that ptr actually point to something. In your case it points to what was created by foo.bar(b);
But foobar(b) makes ptr to point to a new object forgetting the one created by foobar(a)
A more proper code can be
template<class T>
class Foo
{
T* p;
public:
Foo() :p() {}
~Foo() { delete p; }
void bar(T& t) { delete p; ptr = new T(t); }
void bar(const T& t) { delete p; ptr = new T(t); }
void bar(T&& t)
{
if(!ptr) ptr = new T(std::move(t));
else (*ptr) = std::move(t);
}
}
;