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

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

Foo *ptr = new Foo;

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

Foo myfooObject;

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

Ваше здоровье,

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

David Rodríguez - dribeas 19.08.2010 16:29

Обратное, что тоже может быть интересно читателям: stackoverflow.com/questions/124880/…

kevinarpe 16.08.2016 17:37
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
30
2
9 055
9
Перейти к ответу Данный вопрос помечен как решенный

Ответы 9

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

Вы хотите такого поведения?

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

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

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

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

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

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

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

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

Я думаю, что this и dummy всегда будут рядом друг с другом, независимо от того, в куче они или в стеке

Vargas 02.07.2010 00:29

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

pauldoo 02.07.2010 13:01

Вы правы, я ошибочно принял dummy за переменную-член ... извините

Vargas 02.07.2010 22:56

@Ник

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

Например:

struct MyStruct {
    Foo m_foo;
};

MyStruct* p = new MyStruct();

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

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

Ответ принят как подходящий

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

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% способ.

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

kevinarpe 16.08.2016 17:55

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

Patrick Johnmeyer 22.08.2016 21:56

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

Brian Bi 27.09.2018 22:46

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

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

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

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

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

Другие вопросы по теме