Как заставить пользователей создавать объекты класса, производные от моего, только с «новым»? - PullRequest
3 голосов
/ 14 мая 2010

Для реализации подсчета ссылок мы используем 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, равен delete d. Неопределенное поведение в лучшем виде.

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

Ответы [ 3 ]

1 голос
/ 14 мая 2010

Вы можете сделать защищенный деструктор Базового класса и класс интеллектуальных указателей своим другом.

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

1 голос
/ 14 мая 2010

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

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

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

IUnknownLike* obj = createIUnknownLike();
0 голосов
/ 14 мая 2010

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

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

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

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

...