现代C和C ++:可以将一个定义的结构用于其他声明的结构吗?

时间:2015-05-10 14:30:11

标签: c++ c class-design api-design

让我们说我想制作一些应该支持加载图形Image的引擎,所以我有

 struct Image;
 Image* load_image_from_file(...);

我不希望外部世界知道Image究竟是什么,他们只会处理指向它的指针。

但是在engine内,我想使用特定类型,例如SDL_Surface在SDL中完全定义。

我可以以某种方式重新映射此文件的Image,以便编译器在每次看到SDL_Surface*(除宏之外)时都假定为Image*吗?

即。我想要像typedef struct SDL_Surface Image;

这样的东西

所有尝试都是

 using Image = SDL_Surface;
 typedef SDL_Surface Image;
 typedef struct SDL_Surface Image;

产生编译时错误(http://codepad.org/1cFn18oh)。

我知道我可以在struct Image{SDL_Surface* surface}; / engine.c中使用engine.cpp之类的内容,但会创建不必要的间接内容,我必须输入->surface。 另一个肮脏的解决方案是使用显式强制转换,例如((SDL_Surface*)image),但我更清楚地重命名。

PS。我对C和C ++的答案感兴趣。

6 个答案:

答案 0 :(得分:3)

只需定义别名:

using Image = SDL_Surface;
typedef SDL_Surface Image;

which compiles就好了。

如果您需要隐藏SDL_Surface,只需将其导入某个匿名或detail命名的命名空间,并将其用作this

如果由于某些原因,您想要定义自己的Image类型,则可以始终声明一个(n)(隐式)转换函数/运算符,如:

struct Image {
    /* explicit */ operator SDL_Surface() const;
    // ...
};

如果您需要,还可以返回Image

struct Image {
    /* explicit */ Image(SDL_Surface&&);
    /* explicit */ Image(SDL_Surface const&);
    // ...
};

答案 1 :(得分:1)

在C ++ 中,您可以使用继承:

// User view
struct Image;                  // forward declaration (inclomplete type).  
Image* LoadFromFile (...);     // You can use pointer to incomplete type

// Implementation view
struct Image: SDL_Surface { }; // here you go !! :-)

备注:使用类和私有继承会更安全,因此只有Image知道它是SDL_Surface。

在某些情况下,从现有的实现类继承可能是不可取的(例如,如果您需要虚拟析构函数而基类不需要)。然后PIMPL idiom可能是另一种选择(以额外的间接为代价):

//User View unchanged
struct Image;
int TestImage(Image*z); 

//implementation view    
struct Image {
    struct ImageImpl { int x; };  // nested definition or typedef or whatever
    ImageImpl *p;    // works in every case, at cost of an extra indirection instead of a pointer
};
int TestImage(Image* z)
{
return z->p->x;
}

这里PIMPL的主要优点是,您可以展示的不仅仅是不完整的类型,因此为客户提供了一些有用的成员函数。但是如果你不需要这个,并且因为你已经与poitners一起使用客户端的对象,你也可以直接进入组合并拥有ImageImpl成员而不是PIMPL指针。

在C 中,您无法使用继承。但是组合肯定会成功:

struct Image {
   SDL_Surface s; 
   }; 

答案 2 :(得分:1)

此类操作通常使用PIMPL(指向实现的指针)模式完成。但是如果你现在想要避免间接,或者类型是不完整的(这不是SDL_Surface的情况,但它与许多其他SDL类一样)你可以使用指向{{1}的指针因为它可以指向任何数据,然后将其强制转换为实现方。

在此,我们使用void来使用Rule of Zero。这样的std::unique_ptr现在是不可复制的,但可以移动。如果你想复制它,请使用类似Image的指针(不在标准中,但你可以自己轻松编写这样的指针或使用第三方指针)

value_ptr

答案 3 :(得分:0)

如果您的客户端代码对图像没有任何作用,除了传递指针之外,您可以使用Windows API技巧:

each()

答案 4 :(得分:0)

在C 中,您可以采用不完整的类型。

因此,您可以在头文件中定义API:

myapi.h

struct Image;

struct Image* load_image_from_file(...);
...

请注意,您的struct Image类型虽然可供您的客户使用,但却完全对其隐藏。

现在,您的实现完成了结构的完整声明:

myapi.c:

struct Image { 
  /* whatever you want to put here */
  /* even SDL_Surface */
  /* or pointer to it */ 
};

/* function bodies */

将已编译的C代码(对象,静态或动态库)和标头捆绑到客户端。

答案 5 :(得分:0)

在导出的标题中,您可以转发SDL_Surface
然后声明Image是指向它的指针。像:

struct SDL_Surface;
typedef SDL_Surface* Image;
extern Image load_image_from_file(char*);

这样您的库可以在没有SDL标题的情况下使用 但是,仍然需要SDL.dll。