具有变量 return 类型的纯虚函数的功能 - workaround/design?

Functionality of a pure virtual function with variable return type - workaround/design?

我正在开发一个非常非常简单的数据访问层 (DAL),它具有两个 classes:DataTransferObject (DTO) 和 DataAccessObject (DAO)。两个 classes 都是抽象基础 classes,需要针对特定​​用例进行继承和修改。

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

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

在业务逻辑层的 House class 的情况下,DAL classes 的实现将读取以下内容:

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...*/ }
};

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

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

但我不得不发现:虚函数没有自动 return 类型推导。 :(


您对针对此特定案例实施 "virtual function with deduced return type" 有何想法? 我想要在我的 DTO "interface" 中使用通用 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 class 以进一步完善它以供使用,例如Sqlite、XML 文件或其他...我只是介绍了非常基本的概念。

一般来说,您不能在不同的子class中使用virtual函数returning 不同类型,因为这违反了静态类型语言的整个概念:如果你调用 DataTransferObject::toObject(),编译器直到运行时才知道它将成为什么类型 return。

这突出了你设计的主要问题:为什么你需要一个基础 class?你打算如何使用它?调用 DataTransferObject::toObject(),即使您使用一些魔法让它工作(或使用动态类型语言),听起来也是个坏主意,因为您无法确定 return 类型是什么。无论如何,您将需要一些强制转换或一些 ifs 等,以使其正常工作 — 或者您将仅使用所有此类对象的通用功能(HouseRoad 等.) — 但是你只需要一个共同的基础 class 就可以了。

事实上,同一个 return 类型规则有一个例外:如果你 return 一个指向 class 的指针,你可以使用 Covariant return type 概念: subclass 可以将 virtual 函数覆盖为 return 原始 return 类型的 subclass。如果你所有的 "objects" 都有一个共同的基础 class,你可以使用类似

的东西
struct DataTransferObject {
    virtual BaseObject* toObject() = 0;
};

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

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

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

因此,我建议您考虑如何实际 使用 基础 classes(甚至编写一些示例代码),并根据您的选择

如何设置一个空的抽象 class - 实际上,一个接口,然后让你的两种类型都实现它并将其设置为 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:

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

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

更新:如果你想将 classes 分开,按照惯例将数据和操作之间的任何关联分开,你可以将基 class 的名称设置在代表数据即:Building、Construction 等,然后将其用于 toObject.

中的引用类型

你也可以让class在数据操作API上操作那些操作。