具有变量 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 类型是什么。无论如何,您将需要一些强制转换或一些 if
s 等,以使其正常工作 — 或者您将仅使用所有此类对象的通用功能(House
、Road
等.) — 但是你只需要一个共同的基础 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上操作那些操作。
我正在开发一个非常非常简单的数据访问层 (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 类型是什么。无论如何,您将需要一些强制转换或一些 if
s 等,以使其正常工作 — 或者您将仅使用所有此类对象的通用功能(House
、Road
等.) — 但是你只需要一个共同的基础 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上操作那些操作。