Как предотвратить создание объекта в куче?

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

Foo *ptr = new Foo;

и разрешите им это сделать:

Foo myfooObject;

Есть ли у кого-нибудь идеи?

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

Ответ 1

Ответ Ник - хорошая отправная точка, но неполная, поскольку вам действительно нужно перегружать:

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 [] - я бы хотел, но так как они не будут вызваны, это не обязательно.)

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

Ответ 2

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

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

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

Ответ 3

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

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

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

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

Ответ 4

@Nick

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

например:

struct MyStruct {
    Foo m_foo;
};

MyStruct* p = new MyStruct();

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

Ответ 5

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

Ответ 6

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

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

Ответ 7

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

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

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

Ответ 8

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

Это то, что вам нужно?

Ответ 9

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