Как предотвратить создание объекта в куче?
Вопрос
Кто-нибудь знает, как я могу в не зависящем от платформы коде C ++ предотвратить создание объекта в куче?То есть, для класса "Foo" я хочу запретить пользователям делать это:
Foo *ptr = new Foo;
и позволять им делать это только:
Foo myfooObject;
У кого-нибудь есть какие-нибудь идеи?
Ваше здоровье,
Решение
Ответ Ника это хорошая отправная точка, но неполная, так как вам действительно нужно перегрузить:
private:
void* operator new(size_t); // standard new
void* operator new(size_t, void*); // placement new
void* operator new[](size_t); // array new
void* operator new[](size_t, void*); // placement array new
(Хорошая практика кодирования предполагает, что вам также следует перегрузить операторы delete и delete[] - я бы так и сделал, но поскольку они не будут вызываться, это не так в самом деле необходимо.)
Полду также верно, что это не выдерживает агрегирования в Foo, хотя оно выдерживает наследование от Foo.Вы могли бы применить какую-нибудь магию шаблонного метапрограммирования, чтобы помочь предотвратить это, но это не было бы защищено от "злых пользователей" и, следовательно, вероятно, не стоит таких усложнений.Документация о том, как ее следует использовать, и проверка кода, чтобы убедиться, что она используется должным образом, являются единственным ~ 100% способом.
Другие советы
Вы могли бы перегрузить new для Foo и сделать его приватным.Это означало бы, что компилятор застонал бы...если только вы не создаете экземпляр Foo в куче изнутри Foo.Чтобы поймать этот случай, вы могли бы просто не писать новый метод Foo, и тогда компоновщик будет жаловаться на неопределенные символы.
class Foo {
private:
void* operator new(size_t size);
};
PS.Да, я знаю, что это можно легко обойти.Я действительно не рекомендую это - я думаю, что это плохая идея - я просто отвечал на вопрос!;-)
Я не знаю, как сделать это надежным и переносимым способом..но..
Если объект находится в стеке, то вы могли бы утверждать внутри конструктора, что значение 'this' всегда близко к указателю стека.Есть большая вероятность, что объект окажется в стеке, если это так.
Я считаю, что не все платформы реализуют свои стеки в одном и том же направлении, поэтому вы можете захотеть провести одноразовый тест, когда приложение начнет проверять, в какую сторону растет стек..Или сделай какую-нибудь помадку:
FooClass::FooClass() {
char dummy;
ptrdiff_t displacement = &dummy - reinterpret_cast<char*>(this);
if (displacement > 10000 || displacement < -10000) {
throw "Not on the stack - maybe..";
}
}
@Ник
Это можно было бы обойти, создав класс, производный от Foo или агрегирующий его.Я думаю, что то, что я предлагаю (хотя и не является надежным), все равно будет работать для производных и агрегирующих классов.
Например,:
struct MyStruct {
Foo m_foo;
};
MyStruct* p = new MyStruct();
Здесь я создал экземпляр 'Foo' в куче, минуя скрытый новый оператор Foo.
Поскольку заголовки debug могут переопределять сигнатуру operator new, лучше всего использовать ...подписи как комплексное средство правовой защиты:
private:
void* operator new(size_t, ...) = delete;
void* operator new[](size_t, ...) = delete;
Вы могли бы объявить функцию с именем "operator new" внутри класса Foo, которая блокировала бы доступ к обычной форме new .
Это то поведение , которого вы хотите ?
Вы могли бы объявить его как интерфейс и управлять классом реализации более непосредственно из своего собственного кода.
это можно предотвратить, сделав конструкторы закрытыми и предоставив статический элемент для создания объекта в стеке
Class Foo
{
private:
Foo();
Foo(Foo& );
public:
static Foo GenerateInstance() {
Foo a ; return a;
}
}
это позволит создавать объект всегда в стеке.
Не уверен, предлагает ли это какие-либо возможности во время компиляции, но рассматривали ли вы возможность перегрузки оператора 'new' для вашего класса?