2

私の質問の本質はこれです:

ac関数で新しいオブジェクトを作成し、その新しいオブジェクトへの参照を呼び出し元に渡すための最良のアプローチは何ですか?

私の全体的な設定はもう少し複雑で、cとc ++のコードが混在しているため、いくつかの背景情報を含めています。

3Dエンジン用のc++ライブラリを作成しています。このライブラリは、さまざまなプラットフォームのホストアプリケーションから使用することを目的としています。

互換性を簡単にするために、プレーンcのAPIの背後にあるすべてのc++コードを非表示にするつもりです。これは今のところかなりうまく機能しています。

よくわからないのは、cAPIを使用してエンジンインスタンスを実際に作成するための最良の方法です。

私の最初のアプローチはこれでした:

// 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を指します。なんで?

今私の2番目のアプローチはこれでした:

// 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()です。ある場所でオブジェクトを作成し、別の場所でそれを所有することはデリケートなビジネスであることを理解しています。ここで何か間違ったことをしているようですが、どうでしょうか。

アドバイスや助けを事前に感謝します。

4

3 に答える 3

9

あなたのmyEngineCreate関数を見てみましょう:

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

myEngine関数のスコープ内のローカル変数であるため、これは機能しませんmyEngineCreate。C でポインターを「参照渡し」するには、ポインターをポインターとして渡し、参照解除演算子を使用してポインターに代入する必要があります。

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

そして&、ポインターのアドレス演算子を使用して呼び出します。

void * myEngine;
myEngineCreate(&myEngine);

後半の補遺として、別の可能な解決策は、新しいポインターを返すことです。

void* myEngineCreate() {
    retur reinterpret_cast<void*>(new Engine);
}
于 2013-03-14T12:19:28.937 に答える
2

ac関数で新しいオブジェクトを作成し、その新しいオブジェクトへの参照を呼び出し元に渡すための最良の方法は何ですか?

C では、オブジェクト指向の最善の方法は、不透明型とも呼ばれる不完全な型のオブジェクトを作成することです(void ポインターは不透明型ではなく、単なる生のアドレスです)。

例:

// エンジン.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++ 言語のサポートなしで実現できる最高のオブジェクト指向です。呼び出し元は構造体のどのメンバーにもアクセスできません。

しかし、関数が戻った後でも、myEngine は NULL を指しています。なんで?

C および C++ ではポインター自体が値渡しされるためです。ポインターのアドレスを渡すか、呼び出し元にポインターを返す必要があります。このトピックに関する複数の質問と FAQ は、SO で簡単に見つけることができます。

于 2013-03-14T12:25:11.130 に答える
1

不透明なポインターを使用して同様のことを行いました。

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 部分を完全に分離したかったからです。

于 2013-03-14T12:22:10.643 に答える