конструктор — Предотвратить глобальную реализацию?

Есть ли способ заставить экземпляр класса быть создан в стеке или, по крайней мере, предотвратить его глобальность в C ++?

Я хочу предотвратить глобальную реализацию, потому что конструктор вызывает C API, которые требуют предварительной инициализации. У AFAIK нет способа контролировать порядок построения глобальных объектов.

Редактировать: приложение предназначено для встроенного устройства, для которого динамическое выделение памяти также запрещено. Единственное возможное решение для пользователя создать экземпляр класса — либо в стеке, либо с помощью оператора размещения new.

Edit2: мой класс является частью библиотеки, которая зависит от других внешних библиотек (из которых приходят API C). Я не могу изменить эти библиотеки и не могу контролировать способ инициализации библиотек в конечном приложении, поэтому я ищу способ ограничить использование класса.

7

Решение

Вместо того, чтобы накладывать какие-то произвольные ограничения на объекты вашего класса, я бы предпочел сделать вызовы API C безопасными, заключив их в класс. Конструктор этого класса выполняет инициализацию, а деструктор освобождает полученные ресурсы.

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

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

7

Другие решения

Половина ответа:
Чтобы предотвратить выделение кучи (поэтому разрешено только выделение стека), переопределите оператор new и сделайте его закрытым.

void* operator new( size_t size );

РЕДАКТИРОВАТЬ: Другие сказали, просто документировать ограничения, и я вроде согласен, тем не менее, и просто черт побери: нет распределения кучи, нет глобального распределения, API-интерфейсы инициализированы (не совсем в конструкторе, но я бы сказал, все еще достаточно хорошо):

class Boogy
{
public:

static Boogy* GetBoogy()
{
// here, we intilialise the APIs before calling
// DoAPIStuffThatRequiresInitialisationFirst()
InitAPIs();
Boogy* ptr = new Boogy();
ptr->DoAPIStuffThatRequiresInitialisationFirst();
return ptr;
}

// a public operator delete, so people can "delete" what we give them
void operator delete( void* ptr )
{
// this function needs to manage marking array objects as allocated
// or not
}

private:

// operator new returns STACK allocated objects.
void* operator new( size_t size )
{
Boogy* ptr = &(m_Memory[0]);
// (this function also needs to manage marking objects as allocated
// or not)
return ptr;
}

void DoAPIStuffThatRequiresInitialisationFirst()
{
// move the stuff that requires initiaisation first
// from the ctor into HERE.
}

// Declare ALL ctors private so no uncontrolled allocation,
// on stack or HEAP, GLOBAL or otherwise,
Boogy(){}

// All Boogys are on the STACK.
static Boogy m_Memory[10];

};

Я не знаю, горжусь ли я или стыдно! 🙂

1

По сути, вы не можете предотвратить размещение объектов в качестве глобальных. И я бы сказал, что вам не следует пытаться: в конце концов, почему нельзя создать объект, который инициализирует эти библиотеки, создает его глобально, а затем создает глобальный экземпляр вашего объекта?

Итак, позвольте мне перефразировать вопрос, чтобы углубиться в его суть:

Как я могу предотвратить построение моего объекта до того, как будет выполнена некоторая работа по инициализации?

Ответ, как правило, таков: зависит.

Все сводится к тому, что такое инициализация, а именно:

  • Есть ли способ обнаружить это еще не был вызван?
  • Есть ли недостатки в вызове функций инициализации несколько раз?

Например, я могу создать следующее инициализатор:

class Initializer {
public:
Initializer() { static bool _ = Init(); (void)_; }

protected:
// boilerplate to prevent slicing
Initializer(Initializer&&) = default;
Initializer(Initializer const&) = default;
Initializer& operator=(Initializer) = default;

private:
static bool Init();
}; // class Initializer

При первом создании этого класса он вызывает Initи впоследствии это игнорируется (за счет тривиального сравнения). Теперь тривиально унаследовать (приватно) от этого класса, чтобы гарантировать, что к тому времени, когда список инициализатора или тело вашего конструктора будет вызван, требуемая инициализация уже выполнена.

Как должен Init быть реализованным?

Зависит от того, что возможно и дешевле, либо обнаружение инициализации выполнено, либо вызов инициализации независимо.

И если C API настолько дрянной, вы не можете сделать это тоже?

Ты тост. Приветственная документация.

1

Вы можете попробовать использовать одиночка шаблон

0

Есть ли способ заставить экземпляр класса быть создан в стеке или, по крайней мере, предотвратить его глобальность в C ++?

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

Если глобальные переменные были инициализированы до того, как приложение войдет в «main», то вы можете выбросить исключение из конструктора, прежде чем «main» установит какой-либо флаг. Тем не менее, это зависит от реализации, чтобы решить, когда инициализировать глобальные переменные. Таким образом, они могут быть инициализированы после того, как приложение входит в «main». То есть это будет полагаться на неопределенное поведение, что не очень хорошая идея.

Теоретически вы можете попытаться пройтись по стеку вызовов и увидеть, откуда он вызывается. Тем не менее, компилятор может встроить конструктор или несколько функций, и это будет непереносимо, и стековый ход вызова в C ++ будет болезненным.

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

Поэтому я не могу придумать хорошего решения.

В результате лучше всего было бы изменить поведение вашей программы, как уже предлагали другие, — создать одноэлементный класс, который инициализирует ваш C api в конструкторе, деинициализирует его в деструкторе и запрашивает этот класс при необходимости через метод фабрики. Это будет самое элегантное решение вашей проблемы.

Кроме того, вы можете попытаться задокументировать поведение программы.

0

Чтобы выделить класс в стеке, вы просто говорите

FooClass foo; // NOTE no parenthesis because it'd be parsed
// as a function declaration. It's a famous gotcha.

Чтобы выделить в куче, вы говорите

std::unique_ptr<FooClass> foo(new FooClass()); //or
FooClass* foop = new FooClass(); // less safe

Ваш объект будет глобальным, только если вы объявите его в области видимости программы.

-2
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector