С++ для начинающих
508 Resource Limit Is Reached

Resource Limit Is Reached

The website is temporarily unable to service your request as it exceeded resource limit. Please try again later.

Почти виртуальный оператор new


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

NotQuery *pnq;

// установить pnq ...

// оператор new вызывает

// копирующий конструктор NotQuery ...

NotQuery *pnq2 = new NotQuery( *pnq );

Если же у нас есть только указатель на абстрактный класс Query, то задача создания дубликата становится куда менее тривиальной:

const Query *pq = pnq->op();

// как получить дубликат pq?

Если бы позволялось объявить виртуальный экземпляр оператора new, то проблема была бы решена, поскольку автоматически вызывался бы нужный экземпляр. К сожалению, это невозможно: new – статическая функция-член, которая применяется к неструктурированной памяти еще до конструирования объекта класса (см. раздел 15.8).

Но хотя оператор new нельзя сделать виртуальным, разрешается создать его суррогат, который будет выделять память из хипа и копировать туда объекты, – clone():

class Query {

public:

508 Resource Limit Is Reached

Resource Limit Is Reached

The website is temporarily unable to service your request as it exceeded resource limit. Please try again later.

   virtual Query *clone() = 0;

   // ...

};

Вот как он может быть реализован в классе NameQuery:

class NameQuery : public Query {

public:

   virtual Query *clone()

      // вызывается копирующий конструктор класса NameQuery

      { return new NameQuery( *this ); }

   // ...

};

Это работает правильно, если тип целевого указателя Query*:

Query *pq = new NameQuery( "valery" );

Query *pq2 = pq->clone();

Если же его тип равен NameQuery*, нужно привести возвращенный указатель типа Query* назад к типу NameQuery*:

NameQuery *pnq = new NameQuery( "Rilke" );

NameQuery *pnq2 =

    static_cast<NameQuery*>( pnq->clone() );

(Причина, по которой необходимо преобразование типа, объясняется в разделе 19.1.1.)

Как правило, тип значения, возвращаемого реализацией виртуальной функции в производном классе, должен совпадать с типом, возвращаемым ее реализацией в базовом. Исключение, о котором мы уже упоминали, призвано поддержать рассмотренную ситуацию. Если виртуальная функция в базовом классе возвращает значение некоторого типа класса (либо указатель или ссылку на тип класса), то ее реализация в производном может возвращать значение, тип которого является производным от этого класса с открытым типом наследования (то же относится к ссылкам и указателям):

class NameQuery : public Query {

public:

   virtual NameQuery *clone()

      { return new NameQuery( *this ); }

// ...

};

Теперь pq2 и pnq2 можно инициализировать без явного приведения типов:

// Query *pq = new NameQuery( "Broch" );

Query *pq2 = pq->clone();   // правильно

// NameQuery *pnq = new NameQuery( "Rilke" );

NameQuery *pnq2 = pnq->clone();   // правильно

Так выглядит реализация clone() в классе NotQuery:

class NotQuery : public Query {

public:

   virtual NotQuery *clone()

      { return new NotQuery( *this ); }

   // ...

};

Реализации в AndQuery и OrQuery аналогичны. Чтобы эти реализации clone() работали правильно, в классах NotQuery, AndQuery и OrQuery должны быть явно определены копирующие конструкторы. (Мы займемся этим в разделе 17.6.)



Содержание раздела

508 Resource Limit Is Reached

Resource Limit Is Reached

The website is temporarily unable to service your request as it exceeded resource limit. Please try again later.