c++我怎样才能实现这个类结构?

发布于 2024-12-05 14:14:33 字数 2289 浏览 1 评论 0原文

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

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!");
}

ma​​in.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,它在构造时创建平台特定类的实例,所以我得到了无限递归。每次我尝试时,我都会感到非常困惑!

我怎样才能正确实现这样的设计?或者这只是一个可怕的想法。我一直在尝试找出如何编写跨平台类,但我只是得到了大量有关跨平台库的结果,任何帮助将不胜感激:)

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

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;
}

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. 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 :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

太阳公公是暖光 2024-12-12 14:14:33

我认为你想要完成的事情可以更容易完成......

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

等等。我认为这可以以更简单的方式完成您正在尝试的事情。此外,不需要虚拟函数。

I think what you are trying to accomplish can be accomplished much easier...

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

And so on. I think this could accomplish what you are trying in a bit easier fashion. Also, no virtual functions needed.

另类 2024-12-12 14:14:33

从最后开始,是的,确实是一个可怕的想法,就像大多数以“我想避免虚拟函数的成本”开头的想法一样。

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

您所实现的称为“阴影”,您正在使用编译时函数解析。编译器将调用您从中调用的变量的实际类型的 GetPlatformName 函数,因为没有虚拟表可以覆盖指向实际函数的指针。由于mImplementationPlatformIndependantClass,因此mImplementation->GetPlatformName将始终是PlatformIndependantClass::GetPlatformName

编辑:当然,我会想到为什么需要同时创建引擎的 Windows 和 Linux 副本的问题。你永远不会同时使用它们,对吧?

那么为什么不只拥有两个不同的库,每个系统一个,然后从 makefile 链接正确的库。您将获得世界上最好的一切!

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.

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. Since mImplementation is PlatformIndependantClass, mImplementation->GetPlatformName will always be 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. 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. You get the best of all worlds!

只是一片海 2024-12-12 14:14:33

我不使用构造函数来构建特定于平台的实例,而是创建一个静态工厂方法来创建实例:

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

这样就可以避免递归,并且也不需要 mImplementation 指针。

我也会尝试避免特定于平台的类,但那是另一个故事了:)

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.

I would also try to avoid platform-specific classes, but that's another story :)

葮薆情 2024-12-12 14:14:33

当您希望在没有任何运行时开销的情况下实现多态行为时,可以尝试 奇怪的重复模板模式 (CRTP) 。基类是一个模板,派生类使用自身作为基类的模板参数。这需要将您的类定义为模板,这进一步限制了它们只能在头文件 (.hpp) 文件中完全实现。

我不确定如何在您的特定情况下应用该模式。

When you want to have polymorphic behavior without any run-time overhead, you can try the curiously recurring template pattern (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.

I'm not sure how to apply the pattern in your particular case.

另类 2024-12-12 14:14:33

我不认为构造函数会导致无限递归。这是 GetPlatformName() 函数。因为它没有设置为virtual,所以它只能调用它自己。

两种解决方案:使该函数成为虚拟函数,或者完全取消继承。

无论哪种方式,一个函数仅调用另一个函数的成本都会比首先使用虚函数更昂贵。所以我想说保留继承,并虚拟化特定于平台的函数,并直接调用它们,而不需要通过基类函数。

I don't think the constructor is causing the infinite recursion. It's the GetPlatformName() function. 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.

雨后彩虹 2024-12-12 14:14:33

您对无限循环的看法是正确的。修复实际上比您想象的要容易。

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

这里只定义了一个类。在windows中,它只编译并包含windows的东西,在Linux中,它只编译并包含linux的东西。请注意,void* 事物被称为“不透明指针”或“pimpl idiom” http://en.wikipedia.org/wiki/Opaque_pointer

You are correct about the infinte loop. The fix is actually easier than you'd think.

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

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. Note that the void* thing is called an "Opaque pointer" or "pimpl idiom" http://en.wikipedia.org/wiki/Opaque_pointer

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文