2008-08-14 3 views
22

Кто-нибудь знает, как я могу, в платформенно-независимом коде на C++ предотвратить создание объекта в куче? То есть, для класса «Foo», я хочу, чтобы запретить пользователям делать это:Как предотвратить создание объекта в куче?

Foo *ptr = new Foo; 

и только позволяют им делать это:

Foo myfooObject; 

Кто-нибудь есть какие-нибудь идеи?

Приветствие,

+2

Зачем вам это нужно? –

+0

Реверс, который также может быть интересен читателям: http://stackoverflow.com/questions/124880/is-it-possible-to-prevent-stack-allocation-of-an-object-and-only-allow- это должно быть – kevinarpe

ответ

24

Nick's answer является хорошей отправной точкой, но неполно, как вы на самом деле нужно перегрузить:

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 

(практика Хорошо кодирования хотел бы предложить вам также следует перегружать удаление и удаление [] операторы - я бы, но так как они не собираются, чтобы назвать это не действительно необходимо.)

Pauldoo также верно, что это не выживает агрегирование на Foo, хотя это не выжить наследуя от Foo. Вы можете сделать магию мета-программирования шаблона, чтобы ПОМОЩЬ предотвратить это, но это не будет застраховано от «злых пользователей» и, следовательно, вероятно, не стоит осложнений. Документация о том, как его использовать, и обзор кода, чтобы убедиться, что он используется правильно, - это всего лишь 100%.

+1

Будет ли частный конструктор в сочетании с общедоступным статическим заводским методом (возврат по значению) выполнить то же самое? – kevinarpe

+1

@kevinarpe Это зависит от того, насколько мы буквально читаем вопрос. Точный код 'Foo myfooObject;' из вопроса не будет компилироваться, если вы это сделаете. Тем не менее, я предпочитаю подход более похожим на то, что вы предлагаете, если я пытаюсь контролировать, как создаются объекты. –

-1

Вы можете объявить функцию под названием «оператор нового» внутри класса Foo, который будет блокировать доступ к нормальной форме нового.

Это такое поведение, которое вы хотите?

7

Вы можете перегрузить новый для Foo и сделать его приватным. Это означало бы, что компилятор будет стонать ... если вы не создадите экземпляр Foo в куче из Foo. Чтобы поймать этот случай, вы просто не могли бы написать новый метод Foo, а затем компоновщик будет стонать о неопределенных символах.

class Foo { 
private: 
    void* operator new(size_t size); 
}; 

PS. Да, я знаю, что это можно легко обойти. Я действительно не рекомендую это - я думаю, что это плохая идея - я просто отвечал на этот вопрос! ;-)

-1

Не уверен, что это предлагает какие-либо возможности компиляции, но посмотрели ли вы на перегрузку «нового» оператора для вашего класса?

6

Я не знаю, как сделать это надежно и переносимым способом .. но ..

Если объект находится в стеке, то вы могли бы утверждать в конструкторе, что значение ' это 'всегда близко к указателю стека. Есть хороший шанс, что объект будет в стеке, если это так.

Я считаю, что не все платформы реализуют свои стеки в том же направлении, так что вы можете захотеть сделать разовый тест, когда приложение начинает проверять, какой путь стек растет .. Или сделать некоторую помадку:

FooClass::FooClass() { 
    char dummy; 
    ptrdiff_t displacement = &dummy - reinterpret_cast<char*>(this); 
    if (displacement > 10000 || displacement < -10000) { 
     throw "Not on the stack - maybe.."; 
    } 
} 
+0

Интересный подход! – hackworks

+0

Я думаю, что это и манекен всегда будут близки друг к другу независимо от того, находятся они в куче или в стеке – Vargas

+1

@ Варгас - я не согласен. «dummy» всегда будет в стеке, потому что это автоматическая локальная переменная. Указатель 'this' может указывать либо на стек (если FooClass используется как локальная переменная), либо куча (если FooClass выделен в куче или агрегируется внутри класса, который затем распределяется по куче). – pauldoo

3

@Nick

Это можно обойти, создав класс, производный от или агрегирует Foo. Я думаю, что то, что я предлагаю (хотя и не надежное), по-прежнему будет работать на производные и агрегирующие классы.

например:

struct MyStruct { 
    Foo m_foo; 
}; 

MyStruct* p = new MyStruct(); 

Здесь я создал экземпляр «Foo» в куче, минуя скрытый новый оператор Foo в.

+0

+1 Хороший трюк! Тип литья также может помочь – Viet

0

Вы можете объявить его как интерфейс и управлять классом реализации более непосредственно из своего собственного кода.

0

Поскольку отладочные заголовки могут переопределить оператор новую подпись, то лучше использовать ... подписи как полное средство:

private: 
void* operator new(size_t, ...) = delete; 
void* operator new[](size_t, ...) = delete; 
0

это может быть предотвращено путем Конструкторы приватным и обеспечения статического члена создать объект в стеке

Class Foo 
{ 
    private: 
     Foo(); 
     Foo(Foo&); 
    public: 
     static Foo GenerateInstance() { 
      Foo a ; return a; 
     } 
} 

это сделает создание объекта всегда в стеке.

Смежные вопросы