Как заставить пользователей создавать объекты класса, производного от моего, только с помощью new?

Для реализации подсчета ссылок мы используем IUnknown - как интерфейс и класс шаблона умного указателя. В интерфейсе реализованы все методы подсчета ссылок, включая Release():

void IUnknownLike::Release()
{
   if( --refCount == 0 ) {
       delete this;
   }
}

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

 class Class : public IUnknownLike {
 };

 void someFunction( CSmartPointer<Class> object ); //whatever function
 Class object;
 someFunction( &object );

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

Пользователи также могут делать следующее:

struct COuter {
    //whatever else;
    Class inner;// IUnknownLike descendant
};
COuter object;
somefunction( &object.Inner );

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

Есть ли способ изменить интерфейс IUnknownLike, чтобы пользователь был вынужден использовать new для создания всех объектов, производных от IUnknownLike - как прямо производных, так и косвенно производных (с классами между наиболее производными и базовыми)?


person sharptooth    schedule 14.05.2010    source источник


Ответы (3)


Сделайте конструктор закрытым и напишите статическую функцию-член, которая использует новый

class IUnknownLike{
public:
  static IUnknownLike * createIUnknownLike(); { return new IUnknownLike(); }

private:
  IUnknownLike (); // private ctor
};

IUnknownLike* obj = createIUnknownLike();
person Draco Ater    schedule 14.05.2010
comment
Мне нужно получить 100500 классов из интерфейса. Как мне продолжить? - person sharptooth; 14.05.2010
comment
Скорее всего, если вы создаете так много классов, вы, вероятно, автоматизируете создание классов :) - person David Rodríguez - dribeas; 14.05.2010

Вы можете сделать деструктор базового класса защищенным, а класс интеллектуального указателя - своим другом.

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

person Dmitry Yudakov    schedule 14.05.2010
comment
У нас это уже есть. Проблема в том, что никто не создает базу в стеке. Сначала они происходят от базы, и поэтому защита от деструктора базы не помогает. - person sharptooth; 14.05.2010

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

#define FACTORY(NAME) protected: NAME();\
public: static NAME* create ## NAME(){ return new NAME(); }

Если вы хотите передать параметры конструкторам, вам придется стать более привлекательным.

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

person Alex    schedule 14.05.2010
comment
Фактически это запрещает использование конструкторов с параметрами, и это очень прискорбно. - person sharptooth; 17.05.2010