具有可变返回类型的纯虚函数的功能 - 变通方法/设计?

时间:2015-11-06 09:20:25

标签: c++ inheritance virtual-functions return-type-deduction

我正在开发一个非常非常简单的数据访问层(DAL),其中包含两个类:DataTransferObject(DTO)和DataAccessObject(DAO)。这两个类都是抽象基类,需要为特定用例继承和修改。

class DataTransferObject {
protected:
    //protected constructor to prevent initialization
};

class DataAccessObject {
public:
    virtual bool save(DataTransferObject o) = 0;
    virtual DataTransferObject* load(int id) = 0;
};

如果来自业务逻辑层的House类,DAL类的实现将按以下方式读取:

class Dto_House : public DataTransferObject {
public:
    int stories;
    string address;   //...which are all members of the House class...
    Dto_House(House h);
};

class Dao_House : public DataAccessObject {
public:
    bool save(Dto_House h) { /*...implement database access, etc...*/ }
    Dto_House* load(int id) {/*...implement database access, etc...*/ }
};

编辑:当然,派生类知道House类的结构和数据存储。 简单,漂亮,okidoke。 现在,我想在DTO类中提供方法toObject(),以便快速将Dto_House转换为House对象。然后我读了关于C ++ 14中的自动返回类型推导并尝试过:

class DataTransferObject {
public:
    virtual auto toObject() = 0;
};

但我不得不发现:没有虚拟功能的自动返回类型推论。 :(

对于针对此特定情况实施“具有推导返回类型的虚拟函数”,您有什么想法?我想在我的DTO“界面”中使用常规toObject()函数。

我想到的唯一一件事就是:

template <typename T>
class DataTransferObject {
    virtual T toObject() = 0;
};

class Dto_House : public DataTransferObject<House> {
public:
    int stories;
    string address;
    House toObject() {return House(stories, address);}
};

修改 可能的用例是:

House h(3, "231 This Street");
h.doHouseStuff();

//save it
Dto_House dtoSave(h);
Dao_House dao;
dao.save(dtoSave);    //even shorter: dao.save(Dto_House(h));

//now load some other house
Dto_House dtoLoad = dao.load(id 2);
h = dtoLoad.toObject();
h.doOtherHouseStuff();

但是房子不知道它可以保存和装载。

当然,可以导出抽象DAO类以进一步细化它以供例如用于Sqlite,XML文件或其他......我刚才提出了非常基本的概念。

2 个答案:

答案 0 :(得分:1)

如何设置一个空的抽象类 - 实际上是一个接口,然后让两个类型实现它并将其设置为toObject返回引用类型?

class Transferable 
{
    virtual ~Transferable() = 0;
}

然后:

class DataTransferObject {
public:
    //Return a reference of the object.
    virtual Transferable& toObject() = 0;
};

Dto_House : public DataTransferObject, Transferable { /*...*/ }
House     : public DataTransferObject, Transferable { /*...*/ }

上面的例子是我的观点。 更好的是,您可以将DataTransferObject用于此原因作为返回的引用类型,而不是其他抽象类:

class DataTransferObject {
public:
    virtual DataTransferObject& toObject() = 0;
};

Dto_House : public DataTransferObject { /*...*/ }
House     : public DataTransferObject { /*...*/ }

更新:如果您希望将类分开,按惯例分隔数据和操作之间的任何关联,您可以在表示数据的内容上设置基类的名称,即: Building,Construction etc,然后将其用于toObject中的引用类型。

您还可以让类在数据操作API上操作这些操作。

答案 1 :(得分:0)

通常,您不能让virtual函数在不同的子类中返回不同的类型,因为这违反了静态类型语言的整个概念:如果您调用DataTransferObject::toObject() ,编译器不知道它将在运行时返回什么类型。

这突出了你的设计的主要问题:为什么你需要一个基类?你打算如何使用它?调用DataTransferObject::toObject(),即使你使用一些魔术来使其工作(或使用动态类型语言),听起来也不错,因为你无法确定返回类型是什么。无论如何,您将需要一些演员表或一些if等来使其正常工作 - 或者您将仅使用所有此类对象(HouseRoad等常用的功能。) - 但是你只需要为它们所有人提供一个共同的基类。

实际上,相同的返回类型规则有一个例外:如果返回指向类的指针,则可以使用Covariant return type概念:子类可以覆盖virtual函数以返回原始返回类型的子类。如果你所有的&#34;对象&#34;有一个共同的基类,你可以使用

的内容
struct DataTransferObject {
    virtual BaseObject* toObject() = 0;
};

struct Dto_House : public DataTransferObject {
    virtual House* toObject() { /*...*/ } // assumes that House subclasses BaseObject
};

然而,这仍然会留下同样的问题:如果您的代码中只有DataTransferObject,即使您(但不是编译器)知道,它也是{{1}你需要一些演员,这可能是不可靠的。

另一方面,你的模板解决方案似乎相当不错,除了你无法显式调用Dto_House(除非你知道对象的类型),但这是一个坏主意,因为我已解释过。

所以,我建议您考虑如何实际使用基类(甚至编写一些示例代码),并根据它做出选择。