Stop heap allocation via make_shared - c++

I want to force my object to be on the stack to enforce very strict semantics and address some lifetime concerns. I've read a couple articles on how to do this, and arrived at making operator new private (or deleted). This seems to work as expected when new is used directly, but make_shared compiles fine.
#include <boost/smart_ptr.hpp>
class A
{
private:
void *operator new( size_t );
void operator delete( void* );
void *operator new[]( size_t );
void operator delete[]( void* );
};
int main()
{
// A* a = new A; // Correctly produces compile error
boost::shared_ptr<A> a2 = boost::make_shared<A>();
}
Using new A directly gives me this error as expected:
error: ‘static void* A::operator new(size_t)’ is private
I am guessing that make_shared is working because it is using the placement new operator, but I couldn't find any articles that discussed how to prohibit this. The best solution I've come up with is to explicitly delete a template specialization for make_shared
namespace boost
{
template<>
shared_ptr<A> make_shared<A>() = delete;
};
This is obviously very specific to boost::make_shared though. Is this really the best way?

Placement forms of new are pretty easy to handle -- they just come with extra arguments. For example, the simple placement form is
void* operator new(std::size_t, void*);
Note that 18.6.1.3 prohibits redefining these at global scope; however there should be no problem with redefining (or deleting/making inaccessible) them for your particular type.
Unfortunately, make_shared uses scoped ::new (pv) T(std::forward<Args>(args)...). And as I mentioned, you aren't allowed to mess with the global placement new. So you can't prevent it at compile-time, and any runtime trap would be a hack (checking the this pointer to see whether it's in-bounds of the stack).

You cannot enforce that objects of a class are always on the stack by making any operators inaccessible only: Any object which can be constructed on the stack can also be embedded as a member into another object. Even though your original class might struggle against being allocated on the heap the containing class won't. I'd think this is what happens in the case of boost::make_shared(): Internally it probably allocates some record containing both its administration data plus the object actually being allocated. Alternatively, it may use allocation function from some sort of allocator which don't map to the type's operator new() but use its own operator new() overload instead.
I'm not sure if it is possible to prevent heap allocations (at least, when the object is embedded into another object) but any approach doing so would need to make the constructors inaccessible (most likely private) and use some sort of factory functions, possibly in combination with moving. On the other hand, if you can move an object you have an accessible constructor and nothing prevents the object from being moved into an object on the heap.
If you specifically want to prevent the use of std::make_shared() for a concrete type (or boost::make_shared() although I can't quote the rules for specializing the latter), you can specialize std::make_shared(): According to 17.6.4.2.1 [namespace.std] paragraph 1 a user is allowed to specialize any template (unless otherwise specified) if it involves a user-defined type. Thus, you can prevent A from being used with std::make_shared():
class A
{
public:
A();
A(int);
};
namespace std
{
template <> std::shared_ptr<A> make_shared<A>() = delete;
template <> std::shared_ptr<A> make_shared<A, int>(int&&) = delete;
}
namespace boost
{
template <> boost::shared_ptr<A> make_shared<A>() = delete;
template <> boost::shared_ptr<A> make_shared<A, int>(int&&) = delete;
}
Obviously, if you have multiple constructors in A you might need to add more specializations. ... and if your type happens to be a class template or your constructor to be a template, you'll be out of luck: You cannot partially specialize function templates.
With respect to your question about placement new (which may or may not be used by make_shared()): The placement new (and delete) signatures are these:
void* operator new(size_t, void*) noexcept;
void* operator new[](size_t, void*) noexcept;
void operator delete(void*, void*) noexcept;
void operator delete[](void*, void*) noexcept;
(see 18.6 [support.dynamic] paragraph 1). I doubt that making them inaccessible will help you anything, though.

Related

Overalignment of an existing type in c++17

Given an existing type T, it is possible to overalign it on the stack with the alignas() keyword:
alignas(1024) T variable;
For dynamic allocation, we have a cumbersome syntax :
T *variable = new (std::align_val_t(1024)) T;
However, there are two problems with this syntax:
Microsoft's compiler emits error C2956 although the syntax is valid;
There seems to be no corresponding operator for destruction and aligned delete.
A workaround seems would be the definition of a new type that encapsulates T:
alignas(1024)
struct AlignedType {
T _;
};
Alignedtype variable = new AlignedType; // Properly aligned in c++17
delete variable; // Suitable aligned deallocation function called in c++17
This workaround is messy, if T's constructor has parameters, we must add some syntaxic shenanigan to forward constructor arguments, and we need to access variable._ to get the real content.
Inheritance is a bit simpler, but if T is a fundamental type (like uint32_t), we can't use inheritance.
My question is as follow:
Does something like
using AlignedType = alignas(32) T;
is possible (the above does not compile) or is it just not possible to dynamically allocate an existing type using custom alignment without resorting to syntaxic complexities ?
It is not possible to achieve the equivalent of your hypothetical:
using AlignedType = alignas(32) T;
There is no such thing as a type that is equivalent to another type but with different alignment. If the language allowed something like that to exist, imagine all the extra overload resolution rules we would have to add to the language.
The syntax
new (std::align_val_t(1024)) T;
should work. However, MSVC has a bug where it considers ::operator new(std::size_t, std::align_val_t) to be a "placement allocation function" rather than a "usual allocation function". This leads to the error you are seeing, where it complains that a placement allocation function matches a usual deallocation function.
(It seems that it is the standard's fault for not being clear: CWG2592. However, MSVC is to blame too; why did they choose to interpret the standard in a way that makes new (std::align_val_t(x)) T illegal?)
A workaround is to implement your own operator new and matching operator delete that delegate to the ones that you actually want to call:
struct alignment_tag {};
void* operator new(std::size_t size, alignment_tag, std::align_val_t alignment) {
return operator new(size, alignment);
}
void operator delete(void* p, alignment_tag, std::align_val_t alignment) {
operator delete(p, alignment);
}
Now you can do
T *variable = new (alignment_tag{}, std::align_val_t(1024)) T;
I personally would wrap this in something like:
template <class T>
struct AlignedDeleter {
AlignedDeleter(std::align_val_t alignment) : alignment_(alignment) {}
void operator()(T* ptr) const {
ptr->~T();
::operator delete(ptr, alignment_);
}
std::align_val_t alignment_;
};
template <typename T>
using AlignedUniquePtr = std::unique_ptr<T, AlignedDeleter<T>>;
template <typename T, typename... Args>
AlignedUniquePtr<T> make_unique_aligned(std::align_val_t alignment, Args&&... args) {
return AlignedUniquePtr<T>(new (alignment_tag{}, alignment) T(std::forward<Args>(args)...), AlignedDeleter<T>(alignment));
}
(You can always call .release() if you need to.)

C++ custom delete

Let's say we have the following code:
class MyType
{
public: int x;
}
MyType* object = new MyType();
delete object;
Is there a possible to check if object has specific value of x before deletion?
For example, if object.x is odd number, the object should not be free from memory after delete is called. In a few words to make a custom delete for this class where we can choose when the object can be free at operator delete call.
What is the real issue you are trying to solve?
To begin with, this Q&A is a good lesson to the common scenario in C++ of:
The language allowing you to do something, but just because you can doesn't mean you should.
These scenarios are commonly run into when trying to solve an XY problem.
In this particular case, instead of trying to overload operator delete (solving the Y problem, to much confusion of the clients of MyType) you are likely looking for something entirely different (the X problem), say a resource manager that take responsibility of MyType resources w.r.t. if and when they should be deleted (or not), based on e.g. object traits (such as oddness of a data member). See e.g. #Ayxan Haqverdili's answer for a minimal example.
A look at theoretically solving the misidentified Y problem
Don't do this, ever, for this kind of use case.
You can overload operator delete (as well as operator delete[]), by defining it as a static member function of the class for which you want to overload it (lookup precedence by lexical scope). As pointed out in the comments, whilst overloading the usual deallocation functions is legal, trying to conditionally allow for it to be called more than once is not:
struct MyType final {
int x;
static void operator delete(void *p) {
if (static_cast<MyType *>(p)->x % 2 == 1) {
::operator delete(p);
}
}
};
int main() {
MyType *object = new MyType{42};
delete object; // Not DEALLOCATED.
// Underlying object DESTRUCTED, however.
// Extremely confusing.
++(object->x);
delete object; // UB: destructor for underlying object called twice.
}
as per [expr.delete]/6 [emphasis mine]:
If the value of the operand of the delete-expression is not a null pointer value and the selected deallocation function (see below) is not a destroying operator delete, the delete-expression will invoke the destructor (if any) for the object or the elements of the array being deleted.
As a second attempt (emphasis: don't ever go down this road), we could avoid the destructor being called (only calling it conditionally) by overloading and abusing the (new as of C++20) class-specific destroying deallocation functions:
#include <iostream>
#include <new>
struct MyType final {
int x;
static void operator delete(MyType *p, std::destroying_delete_t) {
if (p->x % 2 == 1) {
p->~MyType();
::operator delete(p);
}
}
~MyType() {
std::cout << "\ndtor";
}
};
int main() {
MyType *object = new MyType{42};
delete object; // Neither deallocated nor underlying object destructed.
++(object->x);
delete object; // Destructed and deallocated.
}
Afaict this program is well-formed, but it does abuse the new destroying delete overloads. Clang does emit a -Wmismatched-new-delete warning, which is typically a hint for UB, but it may be inaccurate here.
Anyway, this seems like a good place to stop the fruitless journey of addressing this particular Y problem.
Yes:
if (object->x % 2 == 1)
delete object;
Note that raw pointers should be used rarely and with a lot of care.

Static member placement delete signature?

I have a project that tightly controls what and how can allocate memory. I have a base class for things that may be allocated on a heap, with static overloads for operator new and operator delete and their array variants. These work perfectly without any warnings at all.
There's an ultimate base class for everything that allows placement new only:
class Object
{
public:
static void* operator new(size_t, void*);
static void* operator new[](size_t, void*);
static void operator delete(void*, void*);
static void operator delete[](void*, void*);
};
The implementations are trivial and in the corresponding .cpp file. operator news return the pointer, operator deletes don't do anything.
When I compile this under VS2015, using new (ptr) DerivedFromObject() generates the following warning. Exception handling is set to /EHa.
warning C4291: 'void *Object::operator new(std::size_t,void *)': no matching operator delete found; memory will not be freed if initialization throws an exception
I've tried messing around with the signatures: adding noexcept, adding size_t to operator delete but nothing seems to work. What's the correct form of static member placement operator delete?
It seems that I need to declare operator deletes in every derived class to get rid of the warning.

Not able to create the object by new if new operator is overloaded and constructor is private

Can any body explain why i am not able to create the object using new if it is overloaded and constructor is private.
class A
{
A();
A(A const &obj);
A& operator =(A const &obj);
public:
void * operator new(Size_t size)
{
void * p = malloc (size);
return p ;
}
};
int main ()
{
A * p = new A() /*gives compile time error, saying constructor
is private.But cant we access private function from class
public function . as operator new is public function of class.*/
return 0;
}
Possibly you are conflating a new-expression with operator new.
An operator new is an allocation function, with a confusing name. It should better have been called _alloc or some such.
A new-expression
calls the allocation function, passing any specified arguments.
if that succeeds, calls the class constuctor with specified arguments.
Here the relevant constructor must be accessible.
if that fails, cleans up by deallocating memory (in the case where the allocation function has user defined arguments, a corresponding user defined deallocation function is called with the originally specified custom allocation function arguments).
In short, a new-expression guarantees that if you have fulfilled your obligations, then you will either have an initialized object at hand, or an exception with no memory leak.
One way to not fulfill your obligations is to defined a custom allocation function with no custom deallocation function. Then in the last bullet point above you get no cleanup, and hence a memory leak. But other than that, a new-expression is almost like a database transaction, it guarantees all-or-nothing.
Constructor is called after operator new. As you can see, in operator new you are only using malloc to allocate raw memory. Afterwards, default constructor is called to initialize class members. So new A() calls both, operator new and constructor, constructor is private, so you cannot access it, and thus compiler won't allow it.

Do I need to overload delete if I have operator T *()?

If I have a template class A which holds a pointer, and A has an implicit conversion operator which will return that pointer, do I need to, or should I, define a delete operator for A, if I intent to apply delete to objects of this class?
You only need to define operator delete if you define operator new -- in which case you pretty much must do so.
That doesn't mean that something won't need to delete your A*s -- but you don't need to define any operators for that, it will work by default.
I believe that you've got something like the following:
template <typename T>
struct A {
T * t;
operator T* () { return t; }
};
An you intend to "apply delete to objects of this class", so by that I'm assuming you mean:
void foo ()
{
A<int> * p = new A<int>;
delete p; // Applying 'delete'
}
If this is the case, then the call to delete correctly destroys the object and frees the the memory allocated by the use of 'new' on the previous line and the answer to your question is 'no'.
Because you have declared a conversion operator to a pointer, it is possible to use delete on an object of type A<int> (a opposed to A<int>*). The use of delete will be applied to the result of calling the conversion operator:
void foo ()
{
A<int> a;
a.t = new int;
delete a; // Same as: delete a.operator T*()
}
Basics of how delete works:
The expression delete p, does two different things. Firstly, it calls the destructor for the object pointed to by p and then it frees the memory. If you define an operator delete member for your class then it will be that function which will be used by delete p to free the memory.
In general, you only define those operators when you want to control how the memory for dynamic objects of that class should be allocated or freed.
If your class owns this pointer, it should delete it in its destructor. Be aware that overloading this operator may be confusing, because the usual approach to obtain a pointer to an object is by taking its address.
Does you class A really need to define an implicit conversion operator? Maybe have a simple T* get() const method that returns the pointer, like the boost and std smart pointer do. Implicit conversion can cause all kinds of trouble.