【问题标题】:Are there advantages to the C approach over the C++ approach at times? [closed]C 方法有时比 C++ 方法有优势吗? [关闭]
【发布时间】:2012-02-23 22:42:49
【问题描述】:

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

例如:

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

对比

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

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

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 方法只是为了采用它,但这可能只是一种优先考虑。

【问题讨论】:

  • 我不明白你的意思。这不是面向对象的方法,也许这是一种很好的编程习惯,但是结构的所有成员都是公共的,当然你不能有方法。在 C++ 中,您可以改用类,并利用构造函数、析构函数、方法以及最重要的多态性!
  • typedef struct Foo Foo 是怎么出现的?该结构的存在只是为了减少所需的输入量,并且与程序与 OOP、封装与未封装或您的问题有关。
  • @vulkanino 在 C 中执行此类操作时,您通常在文件范围内将成员声明为静态成员,以创建私有封装。这是一种面向对象的方法,即使不是特别优雅。
  • @Lundin,当然,但我认为这根本不是 OO,它只是尝试封装全局变量,无论如何使用“extern”都可以访问这些变量......看不到真正的优势,我的东西是“工厂”功能:obj.a = a;等等。它将创建与初始化混合在一起......

标签: c++ c oop procedural-programming


【解决方案1】:

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

  • “简单”数据+函数

优于

  • 对象

在某些情况下。

不。是等价的。

除了例外 (!),您用 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;
};

可以认为是面向对象的。

编辑simple and dummy example of polymorphism.

#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!"}

【讨论】:

  • 我会反对 :) 你不能在 C 或任何其他过程语言中以任何方式拥有多态性。
  • @vulkanino:你认为指向函数的指针有什么用;)?
  • @vulkanino:我在 C 中编辑了我的答案以包含多态性。
  • 不,马特 - 除了可读性差 - 调用 print(&foo, &FooPI) 时,调用者必须知道(并创建一个临时)第二个参数类型。这不是类型独立。尝试编写一个带有“可打印”接口对象的打印函数,并使用异构参数(foo 或 bar)调用它两次,这是一种可打印类型。在这里你不能有一个类型,即使它不同,它也表现得像另一种类型,那就是 - 类型独立。
  • @vulkanino:好吧,我不同意 :) 编译器不会为您传递指向虚拟表的指针,因此您必须显式传递它。但是在print 函数中,您不知道传递的“真实”类型。当然,您可以通过将指向对象的指针和指向表的指针包装到单个结构中(ala Go)或声明传入的对象的前 8 个字节始终是指向表的指针并且可以强制转换(ala C++ )。但是,原理保持不变。
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

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

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

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

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

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-04
        相关资源
        最近更新 更多