我正在编写一个旨在跨平台的程序;因此,它将支持某些操作的多个实现。我的第一个想法是编写一个类的层次结构,具有一个通用接口,可能是每个平台的抽象工厂。
class Operation {
DoOperation() = 0;
}
class OperationPlatform1 : public Operation {
DoOperation();
}
class OperationPlatform2 : public Operation {
DoOperation();
}
#ifdef USING_PLATFORM1
Operation *op = new OperationPlatform1;
#endif
但是,我意识到,将在编译时使用将要使用的实现。我试着想一想如何使用静态多态实现这一点,之后我意识到我还可以写下这些内容:
class OperationPlatform1 {
DoOperation();
}
class OperationPlatform2 {
DoOperation();
}
#ifdef USING_PLATFORM1
typedef OperationPlatform1 Operation;
#endif
Operation op;
抽象多个实现的好方法是什么,其中只有一个在编译时被选中?我也对性能感兴趣,所以除非必须,否则我不想使用虚拟方法。
答案 0 :(得分:5)
通常的解决方案是只定义一个类
标题,并为其提供不同的源文件,进行编译
并链接任何必要的。如果你需要一些
您也可以在类定义中依赖(例如数据类型)
通过为每个创建一个特定的头文件来实现这一点
平台,包括它。这里最简单的解决方案是
可能是为每个目标平台创建一个目录,并把所有目录
平台相关文件的位置,使用-I
/ /I
来选择
正确的目录(以及正确的平台)
依赖文件)在编译时。
过度使用#ifdef
通常表明设计不佳。看到
https://www.usenix.org/legacy/publications/library/proceedings/sa92/spencer.pdf
答案 1 :(得分:2)
你有几个选择:
使用您问题中列出的方法。但是,您可能需要将平台特定位拆分为单独的文件,并使用构建规则为平台引入正确的文件。
只需拥有一个类,并使用#ifdef
为相应的平台插入正确的代码。
使用预先存在的包装器库(例如Boost),它可能已经包装了特定于平台的位,并针对包装器进行了编码。
如果您打算封装各种平台,那么您需要确保抽象不会泄漏到您的实现中。通常,您最终会使用列出的三个项目的组合。
答案 2 :(得分:2)
这里有你常用的东西。如果你绝对知道你永远不会同时有多个实现,你可以使用单个头文件,或者有条件编译的实现文件(cpp),或者有平台预编译器指令从编译中排除一些代码(其他平台)。 / p>
你可以在标题中找到:
class Operation {
DoOperation();
}
并且多个cpp 可以从您的构建中排除文件:
Platform1.cpp
Operation::DoOperation() { // Platform 1 implementation }
和Platform2.cpp
Operation::DoOperation() { // Platform 2 implementation }
或单一实施文件:
#ifdef PLATFORM1
Operation::DoOperation() { // Platform 1 implementation }
elseif defined(PLATFORM2)
Operation::DoOperation() { // Platform 2 implementation }
#endif
或者如果你不想从构建中排除文件,那么混合2 :
Platform1.cpp:
#ifdef PLATFORM1
Operation::DoOperation() { // Platform 1 implementation }
#endif
和Platform2.cpp:
#ifdef PLATFORM2
Operation::DoOperation() { // Platform 2 implementation }
#endif
答案 3 :(得分:1)
您想要的是在保持源代码清洁的同时为不同平台提供支持。因此,你应该抽象出像某个界面那样的平台依赖关系,就像你的第一个想法那样。
同时您可能希望使用#ifdef进行实施。例如,文件系统功能在很大程度上取决于平台,因此您将定义一个这样的文件系统接口。
class IFileSystem {
public:
void CopyFile(const string& destName, const string& sourceName);
void DeleteFile(const string& name);
// etc.
};
然后你可以实现WindowsFileSystem,LinuxFileSystem,SolarisFileSystem等。
现在,当您的源代码需要文件系统功能时,您将访问所请求的界面,如下所示:
IFileSystem& GetFileSystem() {
#ifdef WINDOWS
return WindowsFileSystemInstance;
#endif
#ifdef LINUX
return LinuxFileSystemInstance;
#endif
// etc.
}
GetFileSystem().CopyFile("dest", "source");
这种方法可以帮助您分离应用程序逻辑和平台依赖关系的问题。
getter函数的另一个好处是你仍然可以为文件系统实现做运行时选择,例如: Fat32FileSystem,NtfsFileSystem,Ext3FileSystem等。
答案 4 :(得分:0)
查看现有的多平台框架,例如Qt。使用PIMPL idiom