Можно ли предотвратить выделение стека объекта и разрешить его создание только с помощью «new»? - PullRequest
47 голосов
/ 24 сентября 2008

Можно ли предотвратить выделение стека для объекта и разрешить его накапливание только с помощью 'new' в куче?

Ответы [ 6 ]

82 голосов
/ 24 сентября 2008

Один из способов сделать это - сделать конструкторы частными и разрешить конструирование только через статический метод, который возвращает указатель. Например:

class Foo
{
public:
    ~Foo();
    static Foo* createFoo()
    {
        return new Foo();
    }
private:
    Foo();
    Foo(const Foo&);
    Foo& operator=(const Foo&);
};
16 голосов
/ 03 октября 2012

В случае C ++ 11

class Foo
{
  public:
    ~Foo();
    static Foo* createFoo()
    {
        return new Foo();
    }

    Foo(const Foo &) = delete; // if needed, put as private
    Foo & operator=(const Foo &) = delete; // if needed, put as private
    Foo(Foo &&) = delete; // if needed, put as private
    Foo & operator=(Foo &&) = delete; // if needed, put as private

  private:
    Foo();
};
11 голосов
/ 24 сентября 2008

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

5 голосов
/ 20 ноября 2013

Следующее позволяет общедоступные конструкторы и остановит выделение стека, выдавая во время выполнения. Примечание thread_local - это ключевое слово C ++ 11.

class NoStackBase {
    static thread_local bool _heap;
protected:
    NoStackBase() {
        bool _stack = _heap;
        _heap = false;
        if (_stack)
            throw std::logic_error("heap allocations only");
    }
public:
    void* operator new(size_t size) throw (std::bad_alloc) { 
        _heap = true;
        return ::operator new(size);
    }
    void* operator new(size_t size, const std::nothrow_t& nothrow_value) throw () {
        _heap = true;
        return ::operator new(size, nothrow_value);
    }
    void* operator new(size_t size, void* ptr) throw () {
        _heap = true;
        return ::operator new(size, ptr);
    }
    void* operator new[](size_t size) throw (std::bad_alloc) {
        _heap = true;
        return ::operator new[](size);
    }
    void* operator new[](size_t size, const std::nothrow_t& nothrow_value) throw () {
        _heap = true;
        return ::operator new[](size, nothrow_value);
    }
    void* operator new[](size_t size, void* ptr) throw () {
        _heap = true;
        return ::operator new[](size, ptr);
    }
};

bool thread_local NoStackBase::_heap = false;
0 голосов
/ 22 августа 2018

Это должно быть возможно в C ++ 20 с использованием уничтожающего оператора delete, см. p0722r3 .

#include <new>

class C
{
private:
  ~C() = default;
public:
  void operator delete(C *c, std::destroying_delete_t)
  {
    c->~C();
    ::operator delete(c);
  }
};

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

0 голосов
/ 24 сентября 2008

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

// Header file

class IAbstract
{
    virtual void AbstractMethod() = 0;

public:
    virtual ~IAbstract();
};

IAbstract* CreateSubClassA();
IAbstract* CreateSubClassB();

// Source file

class SubClassA : public IAbstract
{
    void AbstractMethod() {}
};

class SubClassB : public IAbstract
{
    void AbstractMethod() {}
};

IAbstract* CreateSubClassA()
{
    return new SubClassA;
}

IAbstract* CreateSubClassB()
{
    return new SubClassB;
}
...