问题
I know I can prevent ordinary heap allocation of custom class and its descendants by making the class's operator new
private, but is there any way to prevent a user of a library from calling std::make_shared
on a custom class (or its descendants)? Apparently, simply making the operator new
private in a class does not stop it.
Note that I do not want to completely prevent shared pointers from being created by any means, as I intend to still be able to produce a std::shared_ptr
for my custom class by calling a static generator method, but I still want to prevent others from trying to call std::make_shared
on my class directly.
EDIT:
To address queries below, my ultimate goal here is a flexible and reusable mechanism for extracting a shared_ptr from a raw pointer. std::enable_shared_from_this
is regretfully not very friendly when it comes to inheritance and especially multiple inheritance.
Also, because I am intending for this to be used as in a template class using CRTP, it would complicate matters somewhat for the a child class T to need to explicitly make the templated parent class it inherits from a friend so that it can access otherwise private constructors.
回答1:
Blocking user heap allocation of a type T
is not actually a thing you can do in C++. At least, not so long as they can create T
s. Even if you manage to forbid make_shared<T>
, your user can still do this:
unique_ptr opt = new optional<T>;
opt->emplace(...);
The T
inside of *opt
is definitely on the heap. Any number of other similar gymnastics can achieve the same effect. You can even call make_shared<optional<T>>
, with the in_place
parameters.
So long as T
has publicly accessible constructors, or there is any publicly accessible way of constructing a T
that returns a prvalue of it, users can find ways to store that T
on the heap.
So the only way to prevent this is to make all of your constructors private (whether directly with private
or a private
key type or whatever other mechanism you want) and only provide publicly accessible functions that return shared_ptr<T>
s.
Outside of macros, there is no C++ mechanism that just causes a type to work this way. It must be done individually, for each type that you want to work this way.
回答2:
This is not specific for std::shared_ptr
, but you can make the constructor private and that way force all instances of the class to get generated from your static method.
#include <iostream>
#include <memory>
class Foo {
private:
Foo() = default;
public:
static std::shared_ptr<Foo> make() {
return std::shared_ptr<Foo>(new Foo);
}
};
int main() {
//Foo f1;
//auto f2 = std::make_shared<Foo>();
//above does not work since the constructor is private
auto h = Foo::make();
}
You can also use deduplicators suggestion and use a private key to make the constructor inaccessible outside of the class.
#include <iostream>
#include <memory>
class Foo {
private:
struct FooKey {};
public:
Foo(FooKey) {};
static std::shared_ptr<Foo> make() {
return std::make_shared<Foo>(FooKey{});
}
};
int main() {
//Foo f1{Foo::FooKey{}};
//auto f2 = std::make_shared<Foo>(Foo::FooKey{});
//above does not work since Foo::FooKey is private
auto h = Foo::make();
}
回答3:
Above answers are better solutions to the problem. But, if you're trying to not let the make_shared make new instance of your class, maybe this is helpful.
#include <iostream>
#include <memory>
using namespace std;
class A {
private:
void * operator new(size_t size) {}
};
template<> shared_ptr<A> make_shared() {
cout << "this was called.\n";
return nullptr;
}
int main() {
A a;
shared_ptr<A> p = make_shared<A>(a);
shared_ptr<A> q = make_shared<A>();
cout << "p: " << p << endl;
cout << "q: " << q << endl;
return 0;
}
'p' gets a object address while 'q' doesn't.
Note: requires -fpermissive option in the compiler.
来源:https://stackoverflow.com/questions/58015983/how-to-block-usage-of-stdmake-sharedt