3

最近我脑子里一直在思考这个问题。我们大多数人都清楚,在 中C,为了创建一个结构,通常在它前面加上 atypedef以避免struct在引用对象之前调用关键字。当然,C 仅限于结构而不是类。为了弥补这一点,C 倾向于使用专用于结构的全局函数来创建面向对象的方法

例如:

typedef struct{
    int foo;
    float bar;
    char* baz;
} SomeStruct;

比。

struct AnotherStruct {
    int foo;
    float bar;
    char* baz;
};

AnotherStructstruct当在函数中声明该类型的对象时,它之前必须有前缀关键字。例如:

int main( ... )
{
   struct AnotherStruct obj1; //correct
   SomeStruct obj2; //correct
   struct SomeStruct obj3; //bad
   AnotherStruct obj4; //bad 
}

在面向对象的方法方面:

typedef struct {
    //member variables here
} SomeStruct;

SomeStruct* SomeStruct_New( int a, int b, int c )
{
    SomeStruct* obj = calloc( sizeof( SomeStruct ), 1 ); //not sure if that's correct -- been a while since I've used calloc.
    obj.a = a;
    obj.b = b;
    obj.c = c;
    return obj;
}

void SomeStruct_Free( SomeStruct* free )
{
    free( free );
}

这些函数在没有包装器的情况下很容易实现——我只是为了举例而使用它们。我的观点是,鉴于您已经可以在 C++ 中创建一个结构,它不需要在typedef没有关键字的情况下声明struct,并且使用与这些结构相关的非封装函数用于面向对象的方法,我很想知道是否在 C++ 中使用 C 编码方法有任何优点,其中包括使用静态全局函数作为私有成员函数,以及将返回指向对象的指针的全局函数构造函数。

这主要是出于好奇,因为有时我觉得采用 C 方法只是为了采用它,但这可能只是一种优先考虑。

4

4 回答 4

3

很难理解这个问题的要点。在我看来,您的主要审讯是:

  • “简单”数据+函数

好于

  • 对象

在某些情况下。

,它是等价的。

除了例外 (!),您用 C++ 表达的任何代码都可以用 C 表达。这只是语法糖的问题,使 C++ 对应物更易于阅读。在反对者加入潮流之前,是的,虚拟桌子可以在 C 中模拟。

不过,我宁愿使用 C++。编译器检查的封装(private)、编译器驱动的重载选择、编译器样板(模板)。它只是语法糖,但如此甜美的糖。

话虽如此:

class Foo {
  Foo() {}

  friend Foo build(int a, int b);
  friend int getA(Foo const& foo);
  friend int getB(Foo const& foo);

  int a;
  int b;
};

可以认为是面向对象的。

编辑多态性的简单和虚拟示例

#include <stdio.h>

// Interface
typedef void (*FunctionPrint)(void const*);

typedef struct {
  FunctionPrint print;
} PrinterInterface;

void print(void const* item, PrinterInterface const* pi) {
  (*pi->print)(item);
}

// Foo
typedef struct { int a; } Foo;

void printFoo(void const* arg) {
  Foo const* foo = (Foo const*)arg;
  printf("Foo{%d}\n", foo->a);
}

PrinterInterface const FooPI = { &printFoo };

// Bar
typedef struct { char* a; } Bar;

void printBar(void const* arg) {
  Bar const* bar = (Bar const*)arg;
  printf("Bar{\"%s\"}\n", bar->a);
}

PrinterInterface const BarPI = { &printBar };

// Main
int main() {
  Foo foo = { 1 };
  Bar bar = { "Hello, World!" };

  print(&foo, &FooPI);
  print(&bar, &BarPI);
}

结果:

Foo{1}
Bar{"Hello, World!"}
于 2012-02-01T08:33:17.857 回答
0

据我所知,这样的声明之所以存在,是因为必须在不可预测的 C 和 C++ 版本的 C 和 C++ 程序中使用一些常见的头文件(主要来自 OS API:想想 windows.h 或“xlib.h”) .

如果按照今天的方式重写此类 API(注意:API 本身,而不仅仅是接口),他们可能不会有这些类型的声明。是一种“混蛋编码”,它使 API 开发人员确定内存映射(当结构绑定到硬件或外部二进制格式时很重要)和“幻数定义”,这些映射不会在不同语言的不同标头中重复。

于 2012-02-01T08:19:43.823 回答
0

不,我认为在 C++ 中没有任何意义。

您希望使用 C 方法的最明显情况是中断和线程回调函数。但是这些可以写成 C++ 中的私有静态成员,这是首选。

通常,在 C 或 C++ 中甚至从函数返回指针都有意义的情况很少。你的例子不是很好的面向对象,你强迫你的类的用户在不需要时使用动态分配。在 C 中编写 OO 代码时,通常将分配留给调用者。

当您不希望调用任何构造函数并因此完全避免使用类时,我可以在实时嵌入式编程中提出一些案例,但在这种情况下您可能根本不会使用 C++。

于 2012-02-01T08:33:24.127 回答
0

就在最近,我开始使用Horde3D,它(或多或少)实现了您所描述的内容。在 C++ 内部实现,在 C 中公开了一个简单的 API。

这种方法对像我一样想通过外语界面重用引擎的人来说很有吸引力。我正在将引擎耦合到 SWI-Prolog,并且由于 Prolog 不遵循 OOP 方向,因此使用(例如)OGRE接口没有任何好处。OGRE 有更多的功能,但简单也有他的优点......

于 2012-02-01T08:34:24.710 回答