2013-08-01 3 views
2

Я хотел бы принудительно установить определенный API для всех классов, производных от базового класса. Обычно вы делаете это с использованием абстрактного базового класса, который имеет чисто виртуальные функции. Однако как вы обрабатываете функции, возвращающие производный тип? Как мне заставить форматировать этот тип функции?Абстрактный базовый класс для производных классов с функциями, возвращающими тип производного класса

struct base 
{ 
    virtual base func() = 0; 
}; 

struct deriv1 : base 
{ 
    deriv1 func(); 
}; 

struct deriv2 : base 
{ 
    deriv2 func(); 
}; 

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

+0

Я не уверен, что вы хотите сделать. Почему вы возвращаете типы по стоимости? Можете ли вы назвать функции реалистичными именами? Обычно при клонировании обычно возвращается ссылка или указатель на производный тип. Если управление памятью вызывает беспокойство, верните умный указатель (например, google shared_ptr). –

+0

В принципе, я никогда не хочу использовать базу или даже полиморфизм. Я просто хочу создать API, чтобы гарантировать, что любой класс, полученный из базы, может работать в одних и тех же ситуациях. В принципе, способ сказать: «Если вы хотите заменить производный 1 на производный код в вашем коде, вам необходимо реализовать эти функции». Это единственный случай для этого. –

+0

И этот конкретный случай использования не стоит проблем или ударов с помощью интеллектуальных указателей и динамической памяти. –

ответ

1

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

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

http://en.wikipedia.org/wiki/Covariant_return_type

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

+0

Ну ... Это грустно. –

1

В вашем примере, func не будет «та же функция», поэтому deriv1 и deriv2 варианты не будут иметь различные виртуальные функции.

К сожалению, нет другой альтернативы, чем возвращать указатель - это не должен быть указатель на динамически распределяемой памяти (можно, например, возвращать указатель на this или static deriv2 anObject; - но это должно быть указатель на base. [Или ссылка, но та же проблема применяется].

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

vector<base*> v; 
... stuff a bunch of `dervi1` or `deriv2` objects into v. 
for(i : v) 
{ 
    base b = i->func(); 
} 

Теперь e Теперь вы отрезаете [нарезанный] ваш deriv1 или deriv2 размером base, иначе вы бы скопировали объект размером больше base в объект размером base - ни один из которых не принесет никакой пользы. [Я предполагаю, что в реальном случае использования для этого deriv1 и deriv2 на самом деле отличаются от base больше аспектов, чем имя объекта - в противном случае это совершенно бессмысленно. И что deriv1 и deriv2 наследуются от base, конечно].

Другими словами, вы не можете скопировать объект неизвестного типа с помощью =. И абсолютно бесполезно иметь виртуальную функцию, если вам нужно знать, какой тип она возвращает.

0

Blockquote

В принципе способ сказать: "Если вы хотите заменить deriv1 с deriv2 в вашем коде, вам нужно реализовать эти функции"

Blockquote

Из приведенной выше цитаты, похоже, что вы хотите что-то вроде этого:

#include <memory> //for unique_ptr 
#include <iostream> 

struct Base 
{ 
    virtual void doX() = 0; 
    virtual void doY() = 0; 
    virtual ~Base(){} 
}; 

struct D1 : Base 
{ 
    virtual void doX() 
    { 
    std::cout << "D1::doX()" << std::endl; 
    } 
    virtual void doY() 
    { 
    std::cout << "D1::doY()" << std::endl; 
    } 
}; 

struct D2 : D1 
{ 
    virtual void doX() 
    { 
    std::cout << "D2::doX()" << std::endl; 
    } 
    virtual void doY() 
    { 
    std::cout << "D2::doY()" << std::endl; 
    } 
}; 

//From this point on you can do various things: 

void driver() 
{ 
    Base* base = new D1;// 
    base->doX(); //Calls D1::doX() 
    D1* d1 = new D2; 
    d1->doX(); //Calls D2::doX() 
} 
// or... 
void driver(Base* base) 
{ 
    //A way to replace Base with D1 with D2 depending 
    // on how driver was called. 
} 

//Finally, maybe you want a factory to create the correct 
// derived type based on which factory was instantiated. 

// Creates family of products, each item representing the base 
// in it's hierarchy - only one shown here... 
struct AbstractFactory 
{ 
    virtual std::unique_ptr<Base> create() const = 0; 
    protected: 
    virtual ~AbstractFactory(){} 
}; 

struct D1Factory : AbstractFactory 
{ 
    //Doesn't matter if <Derived> is returned, because it adheres 
    // to interface of Base (isA base), and correct functions are 
    // found polymorphically 
    virtual std::unique_ptr<Base> create() const 
    { 
    return std::unique_ptr<Base>(new D1); 
    } 
}; 
struct D2Factory : AbstractFactory 
{ 
    //Doesn't matter if <Derived> is returned, because it adheres 
    // to interface of Base (isA base), and correct functions are 
    // found polymorphically 
    virtual std::unique_ptr<Base> create() const 
    { 
    return std::unique_ptr<Base>(new D2); 
    } 
}; 

void driver(const AbstractFactory& factory) 
{ 
    std::unique_ptr<Base> base(factory.create()); 
    base->doX(); 
    base->doY(); 
    //Memory deallocated automagically... 
} 

int main() 
{ 
    driver(D1Factory()); 
    driver(D2Factory()); 
} 

Вы увидите, что это относится к вашей цитате. D2 заменяет D1 плавно с точки зрения водителя ...

+0

Да, но вопрос, который я задаю, заключается не в том, как обрабатывать обычные вызовы функций, а вызовы функций, которые возвращают объект, тип которого равен производному классу. См. Типы возврата в моем примере. –

+0

Моя мысль: зачем вам это нужно? Приведите конкретный пример с настоящими именами. Я думаю, что ваша путаница исходит из языков GC, где на самом деле эти языки возвращают ссылки, которые позже собирают мусор. Это можно достичь, используя стек в C++ (обертывание вашего ресурса с помощью умного указателя). Никакой язык, на мой взгляд, не имеет разрезания - т. Е. Когда производный тип нарезается, он теряет свою виртуальную часть и становится базой. Это приемлемо для ссылок и указателей, потому что их нельзя разрезать. –

Смежные вопросы