问题描述
我听说编写跨平台c ++代码的一种方法是如下定义类(例如,Window类):
window.h
window_win32.cpp
window_linux.cpp
window_osx.cpp
,然后相应地选择实现文件。
但是如果我有相对于os的该类成员该怎么办?就像 HWND
成员一样,用于Win32实现。我不能将其放在 window.h
中,或者当我尝试在Linux上进行编译时,它会生成编译器错误。
我需要 #ifdef
吗?我已经问过类似的问题,但是这个问题更着重于这个特定问题。
还有更多解决方法这个问题-每个都有其优缺点。
1。)使用宏#ifdef,#endif
//注意:不确定在Windows上是否定义了 WINDOWS或 WIN32或其他名称
#ifdef WINDOWS
#include< window.h>
#else
//等。
#endif
class MyClass
{
public:
/ /公开介面...
私人:
#ifdef WINDOWS
HWND m_myHandle;
#else
//等。
#endif
};
优点:
- 程序的最大速度。
缺点:
- 可读性较差。在许多平台上,它可能会变得非常混乱。
- 包括特定于平台的包含项可能会破坏某些东西。 Windows.h定义了许多具有正常名称的宏。
2。)正如已经编写的那样,您可以使用多态性:
//您的班级用户的IMyClass.h:
class IMyClass
{
public:
虚拟〜IMyClass(){}
虚拟void doSomething()= 0;
};
// MyClassWindows.h是一个平台的实现
#include< windows.h>
#include IMyClass.h
class MyClassWindows:public IMyClass
{
public:
MyClassWindows();
virtual void doSomething();
私人:
HWND m_myHandle;
};
// MyClassWindows.cpp为MyClassWindows实现方法
优点:
- 更清晰的代码。
缺点:
- 用户无法直接创建类的实例(尤其是不在堆栈上)。
- 您必须提供用于创建的特殊功能:例如,声明IMyClass * createMyClass();。并在MyClassWindows.cpp和其他平台特定的文件中定义它。在这种情况下(实际上,在整个多态情况下),您还应该定义销毁实例的函数-为了保持习惯用语无论创建它的人也应销毁。
- Little由于使用了虚拟方法,速度变慢了(在如今,除了非常非常特殊的情况外,几乎是微不足道的。)
- 注意:由于RAM碎片问题,在内存有限的平台上分配可能会出现问题。在那种情况下,可以通过为对象使用某种内存池来解决。
3。)PIMPL习惯用法。 p>
// MyClass.h
class MyClass
{
public:
MyClass();
void doSomething();
私有:
struct MyClassImplementation;
MyClassImplementation * m_impl;
}
// MyClassWindows.h
#include< windows.h>
#include MyClass.h
struct MyClassImplementation
{
HWND m_myHandle;
void doSomething();
}
在这种情况下,MyClassImplementation保留了所有需要的内容(至少平台特定数据)并实现所需的内容(同样,特定于平台)。在MyClass.cpp中,您包括平台特定的实现(方法可以是内联的),在构造函数中(或稍后,如果要-只是小心),您可以分配实现,而在析构函数中,您将销毁它。 b
优点:
- 用户可以创建您的类的实例(包括堆栈)(不必担心un /
- 您不需要在MyClass.h中包含特定于平台的标头。
- 您只需添加引用计数器并实现数据共享和/或写时复制,即使它保留了大量数据,也可以轻松地将您的类用作返回值。
缺点:
- 您必须分配实现对象。对象池可以提供帮助。
- 调用方法时,将调用两个方法,并使用一个指针取消引用。仍然,今天应该没问题。
4。)定义一个中性类型,该类型足以保存您的数据。例如long long int。
// MyClass.h
class MyClass
{
public:
MyClass();
void doSomething();
private:
typedef unsigned long long int MyData;
MyData m_data;
};
在实现中(例如MyClassWindows.cpp),您始终需要在MyClass之间进行转换(重新解释转换): MyData和已存储的实际数据。
优点:
- 与第一种方法一样快但是避免使用宏。
- 避免在不需要时分配。
- 避免多次调用。
- 避免
缺点:
- 您必须绝对地110%确保MyClass :: MyData的大小始终至少与存储的数据相同。
- 如果不同的平台存储大小不同的数据,则您除非使用宏,否则空间有限(如果您使用一些项目,可以,但是有数百万个项目...)。在这种情况下,它不会太混乱。
- 处理数据的底层操作-不安全,而不是面向对象操作。但是很快。
所以请使用最适合您的问题的一种。 。和您的个性:3,因为在速度和空间方面,今天的力量相对来说都是四个相对相等的。
I have heard that a way to write Cross Platform c++ code is to define classes as follows (for example, a Window class):
window.h
window_win32.cpp
window_linux.cpp
window_osx.cpp
and then choose the implementation file accordingly.But what if i have members of that class that are relative to the os? Like a HWND
member for the Win32 implementation. I can't put it in the window.h
or when i'd try to compile it on, say, Linux, it'd generate a compiler error.
Do i need to #ifdef
it? I've already asked a similar question but this one is more focused on this particular problem.
There is more ways to solve this problem - each has it's pros and cons.
1.) Use macros #ifdef, #endif
// Note: not sure if "WINDOWS" or "WIN32" or something else is defined on Windows
#ifdef WINDOWS
#include <window.h>
#else
// etc.
#endif
class MyClass
{
public:
// Public interface...
private:
#ifdef WINDOWS
HWND m_myHandle;
#else
// etc.
#endif
};
Pros:
- Maximal speed of program.
Cons:
- Worse readibility. With many platforms it can get really messy.
- Including platform specific includes might break something. windows.h defines many macros with normal names.
2.) As was there already written, you might use polymorphism:
// IMyClass.h for user of your class:
class IMyClass
{
public:
virtual ~IMyClass() {}
virtual void doSomething() = 0;
};
// MyClassWindows.h is implementation for one platform
#include <windows.h>
#include "IMyClass.h"
class MyClassWindows : public IMyClass
{
public:
MyClassWindows();
virtual void doSomething();
private:
HWND m_myHandle;
};
// MyClassWindows.cpp implements methods for MyClassWindows
Pros:
- Much, much more cleaner code.
Cons:
- User cannot create instances of your class directly (especially not on stack).
- You must provide special function for creation: for example declare IMyClass* createMyClass(); and define it in MyClassWindows.cpp and other platform specific files. In that case (well, in fact in this whole polymorphism case) you should also define function which destroys the instances - in order to keep idiom "whoever created it should also destroy".
- Little slowdown because of virtual methods (in these days practically completely insignificant except very, very special cases).
- Note: the allocation can be problem on platforms with limited memory because of problems with RAM fragmentation. In that case, it can be solved by using some kind of memory pool for your objects.
3.) PIMPL idiom.
// MyClass.h
class MyClass
{
public:
MyClass();
void doSomething();
private:
struct MyClassImplementation;
MyClassImplementation *m_impl;
}
// MyClassWindows.h
#include <windows.h>
#include "MyClass.h"
struct MyClassImplementation
{
HWND m_myHandle;
void doSomething();
}
In this case, MyClassImplementation keeps all needed (at least platform specific) data and implements what is needed (again, platform specific). In MyClass.cpp you include the platform specific implementation (methods can be inline), in constructor (or later if you want to - just be careful) you allocate the implementation and in destructor you will destroy it.
Pros:
- User can create instances of your class (including on stack) (no worrying about un/deleted poiners).
- You do not need to include platform specific headers in MyClass.h.
- You can simply add reference counter and implement data sharing and/or copy-on-write which can easily allow to use your class as return value even if it keeps big amount of data.
Cons:
- You must allocate implementation object. Object pool can help.
- When calling a methods, two are called instead and one pointer dereferencing. Still, today shouldn't be any problem.
4.) Define a neutral type, which is big enough to keep your data. For example long long int.
// MyClass.h
class MyClass
{
public:
MyClass();
void doSomething();
private:
typedef unsigned long long int MyData;
MyData m_data;
};
In implementation (e.g. MyClassWindows.cpp) you always need to cast (reinterpret casting) between MyClass::MyData and actual data stored.
Pros:
- As fast as first way but you avoid macros.
- Avoiding allocation if not needed.
- Avoiding multiple method calls.
- Avoiding including platform specific headers in MyClass.h.
Cons:
- You must be absolutely 110% sure that size of MyClass::MyData is always at least same as data stored.
- If different platform stores differently sized data, you are waisting with space (if you use a few items, it's ok, but with millions of them...) unless you use macros. In this case, it won't be so messy.
- It's low level work with data - unsafe, not OOP. But fast.
So use the one which is best fitting to your problem... and your personality :3 because with today's power are all four more or less relatively equal in terms of speed and space.
这篇关于跨平台C ++代码和单个标头-多种实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!