简体   繁体   English

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

[英]c++ How Can I Achieve This Class Structure?

I'm racking my brain trying to find out how to write cross platform classes while avoiding the cost of virtual functions and any kind of ugliness in the platform specific versions of classes. 我正在绞尽脑汁试图找出如何编写跨平台类,同时避免虚拟函数的成本和平台特定版本的类中的任何丑陋。 Here is what I have tried. 这是我尝试过的。

PlatformIndependantClass.hpp PlatformIndependantClass.hpp

class PlatformIndependantClass {
    public:
        PlatformIndependantClass();
        std::string GetPlatformName();
    private:
        PlatformIndependantClass* mImplementation;

};

LinuxClass.hpp LinuxClass.hpp

#include "PlatformIndependantClass.hpp"
class LinuxClass : public PlatformIndependantClass{
    public:
        std::string GetPlatformName();
};

WindowsClass.hpp WindowsClass.hpp

#include "PlatformIndependantClass.hpp"
class WindowsClass : public PlatformIndependantClass {
    public:
        std::string GetPlatformName();
};

PlatformIndependantClass.cpp 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 LinuxClass.cpp

#include "LinuxClass.hpp"
std::string LinuxClass::GetPlatformName() {
    return std::string("This was compiled on linux!");
}

WindowsClass.cpp WindowsClass.cpp

#include "WindowsClass.hpp"
std::string WindowsClass::GetPlatformName() {
    return std::string("This was compiled on windows!");
}

main.cpp 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;
}

Now, this compiles fine but I get a segmentation fault. 现在,这编译得很好但我得到了一个分段错误。 I believe this is because the platform specific classes inherit from PlatformIndependantClass, which on construction, creates an instance of the platform specific class, so I get infinite recursion. 我相信这是因为平台特定的类继承自PlatformIndependantClass,它在构造时创建了特定于平台的类的实例,因此我获得了无限的递归。 Every time I try, I just get extremely confused! 每次我尝试,我都会非常困惑!

How can I achieve a design like this properly? 如何才能正确实现这样的设计? Or is this just a horrible idea. 或者这只是一个可怕的想法。 I have been trying to find out how to write cross platform classes but I just get a load of results about cross platform libraries, any help will be gratefully accepted :) 我一直试图找出如何编写跨平台类,但我只是得到了一些关于跨平台库的结果,任何帮助都将被感激地接受:)

Starting from the end, yes, truly a horrible idea, as are most ideas that start with "I want to avoid the cost of virtual functions". 从最后开始,是的,真的是一个可怕的想法,大多数想法都以“我想避免虚拟功能的成本”开头。

As to why you're getting the segmentation fault (stack overflow specifically), it's because you aren't using virtual functions, but static linking. 至于你为什么会遇到分段错误(特别是堆栈溢出),这是因为你没有使用虚函数,而是使用静态链接。 The compiler doesn't know that mImplementation is anything but a PlatformIndependantClass , so when you try to call return mImplementation->GetPlatformName() you're calling the same function over and over. 编译器不知道mImplementation只是一个PlatformIndependantClass ,所以当你尝试调用return mImplementation->GetPlatformName()你会return mImplementation->GetPlatformName()调用同一个函数。

What you achieved is called shadowing , you're using compile-time function resolution. 你所取得的成就叫做阴影 ,你正在使用编译时功能解析。 The compiler will call the GetPlatformName function of the actual type of the variable you're calling it from , since there's no virtual table to overwrite the pointers to the actual functions. 编译器将调用您正在调用它的变量的实际类型GetPlatformName函数,因为没有虚拟表来覆盖指向实际函数的指针。 Since mImplementation is PlatformIndependantClass , mImplementation->GetPlatformName will always be PlatformIndependantClass::GetPlatformName . 由于mImplementationPlatformIndependantClass ,因此mImplementation->GetPlatformName将始终为PlatformIndependantClass::GetPlatformName

Edit: Of course the question of why you need to create both a Windows and a Linux copy of your engine at the same time comes to mind. 编辑:当然,我想到了为什么你需要同时创建引擎的Windows和Linux副本的问题。 You'll never use both of them at the same time, right? 你永远不会同时使用它们,对吧?

So why not just have two different libraries, one for each system, and link the right one from your makefile. 那么为什么不只是有两个不同的库,每个系统一个,并从您的makefile链接正确的库。 You get the best of all worlds! 你将获得最好的世界!

I think what you are trying to accomplish can be accomplished much easier... 我认为你想要完成的事情可以更轻松地完成......

Object.h: 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 Object.cpp

#include "Object.h"

void Object::DoStuff() { ... }

ObjectWin32.cpp ObjectWin32.cpp

#if WINDOWS

#include "Object.h"

void Object::DoPlatformSpecificStuff() 
{ 
    //Windows specific stuff... 
}

#endif

ObjectLinux.cpp ObjectLinux.cpp

#if LINUX

#include "Object.h"

void Object::DoPlatformSpecificStuff() 
{ 
    //Linux specific stuff... 
}

#endif

And so on. 等等。 I think this could accomplish what you are trying in a bit easier fashion. 我认为这可以通过更简单的方式完成您的尝试。 Also, no virtual functions needed. 此外,不需要虚拟功能。

Instead of using the constructor to build the platform-specific instance, I would create a static factory method to create the instances: 我将创建一个静态工厂方法来创建实例,而不是使用构造函数来构建特定于平台的实例:

PlatformIndependantClass* PlatformIndependantClass::getPlatformIndependantClass() {
    #ifdef TARGET_LINUX
        return new LinuxClass();
    #endif
    #ifdef TARGET_WINDOWS
        return new WindowsClass();
    #endif
}

This way you avoid the recursion, and you also don't need your mImplementation pointer. 这样可以避免递归,也不需要mImplementation指针。

I would also try to avoid platform-specific classes, but that's another story :) 我也会尝试避免特定于平台的类,但这是另一个故事:)

When you want to have polymorphic behavior without any run-time overhead, you can try the curiously recurring template pattern (CRTP) . 当您希望具有多态行为而没有任何运行时开销时,您可以尝试奇怪的重复模板模式(CRTP) The base class is a template, and the derived class uses itself as the template parameter for the base. 基类是模板,派生类将自身用作基础的模板参数。 This requires your classes to be defined as templates, which further restricts them to be implemented completely in the header (.hpp) files. 这需要将您的类定义为模板,这进一步限制它们完全在头(.hpp)文件中实现。

I'm not sure how to apply the pattern in your particular case. 我不确定如何在您的特定情况下应用该模式。

I don't think the constructor is causing the infinite recursion. 我不认为构造函数导致无限递归。 It's the GetPlatformName() function. 这是GetPlatformName()函数。 Because it's not set as virtual, it can only call itself. 因为它没有设置为虚拟,所以它只能调用自身。

Two solutions: Make that function virtual, or do away with the inheritance completely. 两个解决方案:使该功能成为虚拟,或完全取消继承。

Either way, the cost of a function only calling another function will be more expensive than using virtual functions in the first place. 无论哪种方式,仅调用另一个函数的函数的成本将首先比使用虚函数更昂贵。 So I would say keep the inheritance, and virtualize the functions specific to the platform, and call them directly, without going through a base class function. 所以我会说保留继承,虚拟化特定于平台的函数,并直接调用它们,而无需通过基类函数。

You are correct about the infinte loop. 你对infinte循环是正确的。 The fix is actually easier than you'd think. 修复实际上比你想象的要容易。

PlatformIndependantClass.hpp 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 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 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

There's only ONE class defined here. 这里只定义了一个类。 In windows, it only compiles and contains windows stuff, and in Linux, it only compiles and contains linux stuff. 在Windows中,它只编译并包含windows内容,而在Linux中,它只编译并包含linux内容。 Note that the void* thing is called an "Opaque pointer" or "pimpl idiom" http://en.wikipedia.org/wiki/Opaque_pointer 请注意, void* thing被称为“不透明指针”或“pimpl idiom” http://en.wikipedia.org/wiki/Opaque_pointer

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM