我正在绞尽脑汁试图找出如何编写跨平台类,同时避免虚拟函数的成本和平台特定版本的类中的任何丑陋。这是我尝试过的。
PlatformIndependantClass.hpp
class PlatformIndependantClass {
public:
PlatformIndependantClass();
std::string GetPlatformName();
private:
PlatformIndependantClass* mImplementation;
};
LinuxClass.hpp
#include "PlatformIndependantClass.hpp"
class LinuxClass : public PlatformIndependantClass{
public:
std::string GetPlatformName();
};
WindowsClass.hpp
#include "PlatformIndependantClass.hpp"
class WindowsClass : public PlatformIndependantClass {
public:
std::string GetPlatformName();
};
PlatformIndependantClass.cpp
#include "PlatformIndependantClass.hpp"
#include "LinuxClass.hpp"
#include "WindowsClass.hpp"
PlatformIndependantClass::PlatformIndependantClass() {
#ifdef TARGET_LINUX
mImplementation = new LinuxClass();
#endif
#ifdef TARGET_WINDOWS
mImplementation = new WindowsClass();
#endif
}
std::string PlatformIndependantClass::GetPlatformName() {
return mImplementation->GetPlatformName();
}
LinuxClass.cpp
#include "LinuxClass.hpp"
std::string LinuxClass::GetPlatformName() {
return std::string("This was compiled on linux!");
}
WindowsClass.cpp
#include "WindowsClass.hpp"
std::string WindowsClass::GetPlatformName() {
return std::string("This was compiled on windows!");
}
的main.cpp
#include <iostream>
#include "PlatformIndependantClass.hpp"
using namespace std;
int main()
{
PlatformIndependantClass* cl = new PlatformIndependantClass();
cout << "Hello world!" << endl;
cout << "Operating system name is: " << cl->GetPlatformName() << endl;
cout << "Bye!" << endl;
return 0;
}
现在,这编译得很好但是我遇到了分段错误。我相信这是因为平台特定的类继承自PlatformIndependantClass,它在构造时创建了特定于平台的类的实例,因此我获得了无限的递归。每次我尝试,我都会非常困惑!
如何正确实现这样的设计?或者这只是一个可怕的想法。我一直在努力寻找如何编写跨平台类,但我只是获得了大量关于跨平台库的结果,我们将非常感激地接受任何帮助:)
答案 0 :(得分:6)
从最后开始,是的,真的是一个可怕的想法,大多数想法都以“我想避免虚拟功能的成本”开头。
至于你为什么会遇到分段错误(特别是堆栈溢出),这是因为你没有使用虚函数,而是使用静态链接。编译器不知道mImplementation
不是PlatformIndependantClass
,所以当你试图调用return mImplementation->GetPlatformName()
时,你会一遍又一遍地调用同一个函数。
您所取得的成就被称为 shadowing ,您正在使用编译时功能解析。编译器将从中调用您正在调用它的变量的实际类型的GetPlatformName
函数,因为没有虚拟表来覆盖指向实际函数的指针。由于mImplementation
为PlatformIndependantClass
,mImplementation->GetPlatformName
始终为PlatformIndependantClass::GetPlatformName
。
编辑:当然,我想到了为什么你需要同时创建引擎的Windows和Linux副本的问题。你永远不会同时使用它们两个,对吗?
那么为什么不只是有两个不同的库,每个系统一个,并从你的makefile链接正确的库。你会得到最好的世界!
答案 1 :(得分:6)
我认为你想要完成的事情可以轻松完成......
Object.h:
#include <normal includes>
#if WINDOWS
#include <windows includes>
#endif
#if LINUX
#include <linux includes>
#endif
class Object
{
private:
#if WINDOWS
//Windows Specific Fields...
#endif
#if LINUX
//Linux Specific Fields...
#endif
public:
//Function that performs platform specific functionality
void DoPlatformSpecificStuff();
//Nothing platform specific here
void DoStuff();
};
Object.cpp
#include "Object.h"
void Object::DoStuff() { ... }
ObjectWin32.cpp
#if WINDOWS
#include "Object.h"
void Object::DoPlatformSpecificStuff()
{
//Windows specific stuff...
}
#endif
ObjectLinux.cpp
#if LINUX
#include "Object.h"
void Object::DoPlatformSpecificStuff()
{
//Linux specific stuff...
}
#endif
等等。我认为这可以通过更简单的方式完成您的尝试。此外,不需要虚拟功能。
答案 2 :(得分:1)
我不是使用构造函数来构建特定于平台的实例,而是创建一个静态工厂方法来创建实例:
PlatformIndependantClass* PlatformIndependantClass::getPlatformIndependantClass() {
#ifdef TARGET_LINUX
return new LinuxClass();
#endif
#ifdef TARGET_WINDOWS
return new WindowsClass();
#endif
}
这样可以避免递归,也不需要mImplementation指针。
我也会尝试避免特定于平台的类,但这是另一个故事:)
答案 3 :(得分:1)
如果您希望在没有任何运行时开销的情况下拥有多态行为,可以尝试curiously recurring template pattern (CRTP)。基类是模板,派生类将自身用作基础的模板参数。这需要将您的类定义为模板,这进一步限制了它们在头文件(.hpp)中完全实现。
我不确定如何在您的特定情况下应用该模式。
答案 4 :(得分:0)
我认为构造函数不会导致无限递归。这是GetPlatformName()函数。因为它没有被设置为虚拟,所以它只能自己调用。
两个解决方案:将该功能设为虚拟,或完全取消继承。
无论哪种方式,仅调用另一个函数的函数的成本将首先比使用虚函数更昂贵。所以我想说保留继承,虚拟化特定于平台的函数,并直接调用它们,而不需要通过基类函数。
答案 5 :(得分:0)
你对infinte循环是正确的。修复实际上比你想象的要容易。
PlatformIndependantClass.hpp
#include //portable headers
struct PlatformDependantClass; //defined in Cpp file
class PlatformIndependantClass {
public:
PlatformIndependantClass();
~PlatformIndependantClass();
std::string GetPlatformName();
private:
std::unique_ptr<PlatformDependantClass> mImplementation; //note, different type
};
LinuxClass.cpp
#ifdef __GNUC__
#include //linux headers
#include "PlatformIndependantClass.hpp"
struct PlatformDependantClass { //linux only stuff
//stuff
};
PlatformIndependantClass() {
mImplementation.reset(new PlatformDependantClass );
}
~PlatformIndependantClass() {
}
std::string PlatformIndependantClass::GetPlatformName() {
return std::string("This was compiled on linux!");
}
#endif //__GNUC__
WindowsClass.cpp
#ifdef _MSC_VER
#include //windows headers
#include "PlatformIndependantClass.hpp"
struct PlatformDependantClass { //windows only stuff
//stuff
};
PlatformIndependantClass() {
mImplementation.reset(new PlatformDependantClass );
}
~PlatformIndependantClass() {
}
std::string PlatformIndependantClass::GetPlatformName() {
return std::string("This was compiled on Windows!");
}
#endif //_MSC_VER
这里只定义了 ONE 类。在Windows中,它只编译并包含windows内容,而在Linux中,它只编译并包含linux内容。请注意,void*
事物被称为“不透明指针”或“pimpl idiom”http://en.wikipedia.org/wiki/Opaque_pointer