【问题标题】:How to create a new object in a c function and pass a reference to it?如何在 c 函数中创建一个新对象并传递对它的引用?
【发布时间】:2013-03-14 12:09:53
【问题描述】:

我的问题的本质是这样的:

在 c 函数中创建新对象并将对该新对象的一些引用传递给调用者的最佳方法是什么?

我的整体设置有点复杂,涉及到混合的 c 和 c++ 代码,所以我包括一些背景信息:

我正在为 3D 引擎创建一个 c++ 库。 该库旨在用于不同平台上的主机应用程序。

出于简单的兼容性原因,我现在打算将所有 c++ 代码隐藏在纯 c 中的 API 后面。 到目前为止,这工作得很好。

我唯一不确定的是,这是通过我的 c API 实际创建我的引擎实例的最佳方式。

我的第一个方法是这样的:

// in myEngineAPI.h
void myEngineCreate(void * myEngine);
void myEngineRelease(void * myEngine);


// in myEngineAPI.cpp
#include "myEngineAPI.h"
#include "myPrivateCppEngine.h"

void myEngineCreate(void * myEngine) {
    myEngine = new Engine;               // <- this doesn't seem to work as expected
}
void myEngineRelease(void * myEngine) {
    delete ((Engine *)myEngine);
}


// in my host application
#include "myEngineAPI.h"

void * myEngine = NULL;
myEngineCreate(myEngine);    // <- problem: myEngine is still NULL after this line.

// ...draw some fancy stuff...

myEngineRelease(myEngine);

我希望myEngineCreate(void * myEngine) 将我新创建的对象的地址分配给myEngine。但是函数返回后,myEngine 仍然指向 NULL。为什么?

现在我的第二种方法是:

// in myEngineAPI.h
void * myEngineCreate();
void myEngineRelease(void * myEngine);


// in myEngineAPI.cpp
#include "myEngineAPI.h"
#include "myPrivateCppEngine.h"

void * myEngineCreate() {
    return new Engine;               // <- ok, the function returns a valid pointer
}
void myEngineRelease(void * myEngine) {
    delete ((Engine *)myEngine);
}


// in my host application
#include "myEngineAPI.h"

void * myEngine = myEngineCreate();  // <- yay, I have a void pointer to my engine

// ...draw some fancy stuff...

myEngineRelease(myEngine);

这行得通。 myEngineCreate() 给了我一个指向我的引擎实例的不透明指针,我可以在随后的绘图调用中使用它,也可以将它交给我的释放函数,当我完成它时清理内存。 这种方法的问题是,我的分析器抱怨myEngineCreate() 中的内存泄漏。 我知道在一个地方创建一个对象并在另一个地方拥有它是一件微妙的事情,我似乎在这里做错了什么 - 但是什么?

提前感谢您的任何建议或帮助。

【问题讨论】:

  • 如果您需要使用泛型,请在 C++ 中使用templates,但尽可能避免使用void*!这很容易出错。
  • 我假设宿主应用程序是用 C 编写的,myengine 是用 c++ 编写的,因此使用 void*,只要你不要忘记调用 myEngineRelease() 第二种方法不应该是一个问题,对于第一种方法,请参阅下面 Joachim Pileborg 的答案(但当然你仍然必须在某些时候删除该对象)

标签: c++ c


【解决方案1】:

让我们使用你的myEngineCreate 函数:

void myEngineCreate(void * myEngine) {
    myEngine = new Engine;
}

这不起作用,因为myEnginemyEngineCreate 函数范围内的局部变量。要在 C 中“通过引用”传递指针,您必须将其作为指向指针的指针传递,并使用 derefernece 运算符分配给指针:

void myEngineCreate(void ** myEngine) {
    *reinterpret_cast<Engine**>(myEngine) = new Engine;
}

你可以使用指针的地址运算符&amp;来调用它:

void * myEngine;
myEngineCreate(&myEngine);

作为后期补充,另一种可能的解决方案是返回新指针:

void* myEngineCreate() {
    retur reinterpret_cast<void*>(new Engine);
}

【讨论】:

  • 虽然这个答案很好地解释了为什么当前代码不起作用,但请注意这不是 C 中的最佳解决方案,因为没有类型安全。
  • 对,第一种方法现在有效。感谢您指出。尽管如此,我仍然会遇到内存泄漏,我将进行更多测试...
  • 好的,我修复了我的泄漏。结果证明是无关的。
【解决方案2】:

在 c 函数中创建新对象并将对该新对象的一些引用传递给调用者的最佳方法是什么?

在 C 中,最好的面向对象的方法是创建一个不复杂类型的对象,也称为不透明类型(空指针不是不透明类型,它只是一个原始地址)。

例子:

// 引擎.h

typedef struct engine_t engine_t;

engine_t* engine_create (void);

// 引擎.c

struct engine_t
{
  /* all variables placed here will have true private encapsulation */
};


engine_t* engine_create (void)
{
  return malloc(sizeof(engine_t));
}

// main.c

int main()
{
  engine_t* my_engine = engine_create();
}

这是 C 中真正的面向对象,在没有 C++ 语言支持的情况下可以实现的最佳 OO。调用者将无法访问该结构的任何成员。

但是函数返回后,myEngine 仍然指向 NULL。为什么?

因为指针本身在 C 和 C++ 中是按值传递的。您要么必须传递指针的地址,要么将指针返回给调用者。可以在 SO 上轻松找到有关此主题的多个问题和常见问题解答。

【讨论】:

  • 您的回答是最详细的,并且提供了很多有趣的细节。尽管如此,到目前为止,我仍然坚持 void* 这似乎是最简单的解决方案,我现在真的不需要更多。感谢您指出 void 指针和不透明类型之间的区别。
【解决方案3】:

我使用不透明指针做了类似的事情:

typedef struct my_c_engine *myCEngine;

extern "C" myCEngine createEngine() {
    return reinterpret_cast<myCEngine>(new myRealEngineClass());
}

extern "C" void releaseEngine(myCEngine c) {
    if(c) {
        myRealEngineClass *x = reinterpret_cast<myRealEngineClass*>(c);
        delete x;
    }
}

我这样做是因为我希望 C 部分完全隔离。

【讨论】:

  • 我不希望将指针语法隐藏在 typedef 后面,它读起来很混乱。另附注:无论您使用 new/delete 还是 malloc()/free(),都不需要检查 c 是否为 NULL。 delete 和 free() 都允许 NULL 作为参数。
  • 感谢您在示例中添加 extern "C"。我也必须将此添加到我的代码中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多