c ++如何实现此类结构?

时间:2011-09-20 18:57:02

标签: c++ class inheritance cross-platform

我正在绞尽脑汁试图找出如何编写跨平台类,同时避免虚拟函数的成本和平台特定版本的类中的任何丑陋。这是我尝试过的。

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,它在构造时创建了特定于平台的类的实例,因此我获得了无限的递归。每次我尝试,我都会非常困惑!

如何正确实现这样的设计?或者这只是一个可怕的想法。我一直在努力寻找如何编写跨平台类,但我只是获得了大量关于跨平台库的结果,我们将非常感激地接受任何帮助:)

6 个答案:

答案 0 :(得分:6)

从最后开始,是的,真的是一个可怕的想法,大多数想法都以“我想避免虚拟功能的成本”开头。

至于你为什么会遇到分段错误(特别是堆栈溢出),这是因为你没有使用虚函数,而是使用静态链接。编译器不知道mImplementation不是PlatformIndependantClass,所以当你试图调用return mImplementation->GetPlatformName()时,你会一遍又一遍地调用同一个函数。

您所取得的成就被称为 shadowing ,您正在使用编译时功能解析。编译器将从中调用您正在调用它的变量的实际类型的GetPlatformName函数,因为没有虚拟表来覆盖指向实际函数的指针。由于mImplementationPlatformIndependantClassmImplementation->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