2012-03-26 5 views
2

lib 용 C 래퍼 API를 작성하고 있습니다.C++ : C에서 템플릿 클래스의 포인터 전달

나는 일반적으로 내 C++ 객체를 C에서 void*으로 전달합니다. 그리고 모든 객체의 공용 함수에 대해 자연스럽게 액세스 래퍼 함수가 있습니다. C 코드 네이티브 C++ 클래스 멤버에 액세스하지 않습니다.

어제, IRC에서 C++ 템플릿 클래스의 포인터를 C로 옮겨서는 안되므로 언급했기 때문에 위험합니다. C는 void*입니다. 사실입니까? 템플릿 클래스에 대한 포인터에서 일반적인 C++ 클래스에 대한 포인터는 얼마나 다른가요?

감사합니다.

+3

멤버 함수 포인터를'void *'로 넘겨서는 안됩니다. 또는 함수 포인터라고 생각합니다. 또한 강력한 형식의 핸들 ('typedef struct foo foo; foo * create_foo(); void destroy_foo (foo *); 등)을 void * 대신 사용하는 것을 고려하십시오. –

+0

흠, 강력하게 형식화 된 핸들은 아마도 더 좋을 것입니다. 어떻게 래퍼 코드에 맞는 지 보겠습니다 ... – kralyk

답변

1

포인터 Foo*void*에 캐스트 한 다음 동일한 유형의 Foo*으로 복원하는 것이 안전합니다.그러나 상속을 사용할 때는 특별한주의를 기울여야합니다. 업 캐스팅/다운 캐스팅은 void* 포인터를 통해 수행하면 안됩니다. 또한 this 게시물 void*을 통해 캐스팅 몇 가지 문제를 보여줍니다

#include <cassert> 

class Parent 
{ 
    int bar; 
}; 

class Derived : public Parent 
{ 
    virtual void foo() { } 
}; 

int main() 
{ 
    Derived d; 
    Derived* ptr_derived = &d; 
    void *ptr_derived_void = ptr_derived; 
    Derived* ptr_derived_from_void = (Derived*)ptr_derived_void; 

    assert(ptr_derived_from_void == ptr_derived); //that's OK 

    Parent* ptr_parent = ptr_derived; //upcast 
    Parent* ptr_parent_from_void = (Parent*)ptr_derived_void; //upcast? 

    assert(ptr_parent_from_void == ptr_parent); //that's not OK 

    return 0; 
} 

다음 코드를 살펴 보자.

7

가짜입니다. 템플릿에는 일반 클래스에는없는 특수한 속성이 없습니다. 항상 적절한 캐스팅을 사용하면 괜찮을 것입니다.

0

템플릿 args로 AB 클래스로 인스턴스화 된 템플릿 클래스는 컴파일러가 이러한 개별 유형을 특별히 처리하는 두 개의 별도 클래스를 만드는 과정을 포함합니다. 여러면에서 이것은 지능형 강력하게 형식화 된 선처리 매크로와 같습니다. AB에서 각각 작동하는 두 개의 개별 "일반"클래스를 수동으로 복사하여 붙여 넣는 것과 그다지 다르지 않습니다. 그래서 아니야.

MyClass<A> 

MyClass<B> 

AB하지 않은 경우, 그들은 다른 메모리 레이아웃을 가질 수 있기 때문에 : 원래 무엇 캐스팅해야 할 경우 위험 할 것

유일한 방법은 .

2

이것은 일반 템플릿과는 관계가 없지만 클래스가 다중 상속을 갖고있는 경우 void*으로 전송하기 전에 항상 동일한 유형으로 시작해야하며 다시 캐스팅 할 때도 마찬가지입니다. 포인터의 주소는 포인터 유형이 상위 클래스에 따라 변경됩니다.

class ParentA 
{ 
    // ... 
    int datumA; 
}; 

class ParentB 
{ 
    // ... 
    int datumB; 
}; 

class Derived : public ParentA, public ParentB 
{ 
    // ... 
}; 

int main() 
{ 
    Derived d; 
    ParentA * ptrA = &d; 
    ParentB * ptrB = &d; 
    assert((void*)ptrA == (void*)ptrB); // asserts! 
} 
+0

@Nawaz, 시도해 보셨습니까? VS 2010에서 해냈습니다. C 스타일의 캐스트를 사용하든, reinterpret_cast를 사용하든 별 차이가 없습니다. –

0

포인터는 데이터 포인터입니다. 포인터의 "유형"은 실제로 컴파일 타임에 아무런 차이가 없으며 단지 코드의 가독성 및 유지 보수성을위한 것입니다. 예를 들어

:

Foo<a> *x = new Foo<a>(); 
void *y = (void*)x; 
Foo<a> *z = (Foo<a>*)y; 

완벽하게 유효하고 어떤 문제가 발생할 수 없습니다. 포인터 유형을 캐스팅 할 때 유일한 문제는 포인터가 참조하는 기본 데이터 유형이 실제로 무엇인지 잊어 버렸을 때 역 참조 문제가 발생할 수 있다는 것입니다.

어디서나 void *를 전달하는 경우 유형 무결성을 유지해야합니다. 사고로

Foo<a> *x = new Foo<a>(); 
void *z = (void*)x; 
//pass around z for a while, then mistakenly... 
Foo<b> *y = (Foo<b>*)z; 

:

는 그런 짓을하지 마십시오!

+0

이것은 사실이 아닙니다. 상속이 포함될 때 포인터를 형 변환하는 것은 수퍼 클래스 또는 하위 클래스 데이터를 가리 키도록 메모리의 값을 조정하는 것과 관련 될 수 있습니다. 그러나 그것은 템플릿과 아무 관련이 없습니다. 즉, 적절한 운영자와의 전송에주의해야합니다. – StilesCrisis