C ++에서 C 구조물을 서브 클래스하고 C 코드의 구조물에 포인터를 사용하는 것이 가능합니까?

StackOverflow https://stackoverflow.com/questions/127290

  •  02-07-2019
  •  | 
  •  

문제

이를 수행하는 데 부작용이 있습니까?

C 코드 :

struct foo {
      int k;
};

int ret_foo(const struct foo* f){ 
    return f.k; 
}

C ++ 코드 :

class bar : public foo {

   int my_bar() { 
       return ret_foo( (foo)this ); 
   }

};

있습니다 extern "C" C ++ 코드 주변과 각 코드는 자체 컴파일 장치 내에 있습니다.

이것은 컴파일러 전체에서 휴대용입니까?

도움이 되었습니까?

해결책

이것은 전적으로 합법적입니다. C ++에서 클래스 및 스트러크는 모든 구조물 멤버가 기본적으로 공개되는 것을 제외하고 동일한 개념입니다. 그것이 유일한 차이점입니다. 따라서 구조물을 확장 할 수 있는지 묻는 것은 클래스를 연장 할 수 있는지 묻는 것과 다르지 않습니다.

여기에 하나의 경고가 있습니다. 거기 있습니다 보증이 없습니다 컴파일러에서 컴파일러로의 레이아웃 일관성 따라서 C ++ 코드와 다른 컴파일러로 C 코드를 컴파일하면 멤버 레이아웃 (특히 패딩)과 관련된 문제가 발생할 수 있습니다. 동일한 공급 업체의 C 및 C ++ 컴파일러를 사용할 때도 발생할 수 있습니다.

가지다 GCC 및 G ++에서 이런 일이 발생했습니다. 나는 몇 가지 큰 구조를 사용한 프로젝트에서 일했습니다. 불행하게도, G ++는 GCC보다 스트러크를 상당히 느슨하게 포장하여 C와 C ++ 코드 사이에 객체를 공유하는 중요한 문제를 일으켰습니다. 우리는 결국 C 및 C ++ 코드가 structs를 동일하게 처리하도록 포장 및 삽입 패딩을 수동으로 설정해야했습니다. 그러나이 문제는 서브 클래싱에 관계없이 발생할 수 있습니다. 실제로 우리는이 경우 C 구조물을 서브 클래싱하지 않았습니다.

다른 팁

나는 그런 이상한 서브 클래스를 사용하는 것이 좋습니다. 상속 대신 구성을 사용하도록 설계를 변경하는 것이 좋습니다. 그냥 한 회원을 만드십시오

foo* m_pfoo;

변호사 클래스에서는 같은 일을 할 것입니다.

당신이 할 수있는 다른 일은 해당 getter 방법과 함께 구조 자체를 포함하는 클래스 FoOwRapper를 하나 더 만드는 것입니다. 그런 다음 래퍼를 서브 클래스 할 수 있습니다. 이런 식으로 가상 소멸자의 문제는 사라졌습니다.

"콘크리트 클래스에서 파생하지 마십시오." - Sutter

"잎이 아닌 수업을 추상화하십시오." - 메이어스

인터페이스가 아닌 클래스를 서브 클래스하는 것은 단순히 잘못된 것입니다. 라이브러리를 리팩터링해야합니다.

기술적으로, 당신은 당신이 원하는 것을 할 수 있습니다. 예를 들어, 기본 클래스 서브 버젝트에 대한 포인터로 파생 클래스에 대한 포인터를 삭제하여 정의되지 않은 행동을 호출하지 않는 한, 당신은 당신이 원하는 것을 할 수 있습니다. 당신은 필요조차 없습니다 extern "C" C ++ 코드의 경우. 예, 휴대용입니다. 그러나 그것은 디자인이 좋지 않습니다.

이것은 다른 프로그래머에게는 혼란 스러울 수 있지만 완벽하게 합법적입니다.

상속을 사용하여 방법 및 생성자로 C- 스트러크를 확장 할 수 있습니다.

샘플 :

struct POINT { int x, y; }
class CPoint : POINT
{
public:
    CPoint( int x_, int y_ ) { x = x_; y = y_; }

    const CPoint& operator+=( const POINT& op2 )
    { x += op2.x; y += op2.y; return *this; }

    // etc.
};

스트러크를 확장하는 것은 "더"악일 수 있지만, 당신이 금지하는 것이 아닙니다.

와우, 그게 사악 해요.

이것은 컴파일러 전체에서 휴대용입니까?

가장 확실하지 않습니다. 다음을 고려하세요:

foo* x = new bar();
delete x;

이것이 작동하기 위해서는 Foo의 소멸자가 명확하지 않은 가상이어야합니다. 사용하지 않는 한 new 그리고 파생 된 Objectd에 맞춤형 소멸자가없는 한 운이 좋을 수 있습니다.

/편집 : 반면에, 코드가 질문에서만 사용되는 경우 상속은 구성보다 이점이 없습니다. m_pgladiator가 제공 한 조언을 따르십시오.

이것은 완벽하게 합법적이며 MFC Crect 및 CPoint 클래스에서 실제로 볼 수 있습니다. cpoint는 포인트 (Windef.h에 정의 됨)에서 파생되고 Crect는 rect에서 파생됩니다. 당신은 단순히 멤버 함수로 객체를 장식하고 있습니다. 더 많은 데이터로 객체를 확장하지 않는 한 괜찮습니다. 실제로, 기본 정보를 기본 이니셔티브 화하는 통증 인 복잡한 C 구조물이있는 경우, 기본 생성자가 포함 된 클래스로 확장하는 것은 해당 문제를 다루는 쉬운 방법입니다.

이렇게하더라도 :

foo *pFoo = new bar;
delete pFoo;

그러면 당신은 당신의 생성자와 소멸자가 사소하고 추가 메모리를 할당하지 않았기 때문에 괜찮습니다.

C ++를 실제로 전달하지 않기 때문에 C ++ 객체를 'Extern "C"로 감싸지 않아도됩니다. 유형 C 함수에.

나는 그것이 반드시 문제라고 생각하지 않습니다. 동작은 잘 정의되어 있으며, 수명 문제에주의를 기울이는 한 (C ++와 C 코드 간의 할당을 혼합하고 일치하지 않는 한) 원하는 작업을 수행합니다. 컴파일러 전체에서 완벽하게 휴대 할 수 있어야합니다.

소멸자의 문제는 실제적이지만 기본 클래스 소멸자가 C 구조만이 아니라 가상이 아닐 때마다 적용됩니다. 그것은 당신이 알아야 할 것이지만이 패턴을 사용하는 것을 배제하지는 않습니다.

그것은 작동하고 휴대 할 수 있지만 가상 함수 (소멸자 포함)를 사용할 수는 없습니다.

나는 이것을하는 대신 bar가 foo를 포함하는 것이 좋습니다.

class Bar
{
private:
   Foo  mFoo;
};

나는 당신이 단순히 ret_foo를 회원 방법으로 만드는 이유를 알지 못합니다. 현재의 방식은 코드가 끔찍하게 이해하기 어렵게 만듭니다. 멤버 변수 및 GET/SET 메소드를 사용하여 처음에 실제 클래스를 사용하는 것이 어려운 점은 무엇입니까?

나는 C ++에서 structs를 서브 클래스 할 수 있다는 것을 알고 있지만, 위험은 누군가가 실제로 그것을하는 것이 거의 없기 때문에 다른 사람들이 당신이 코딩 한 것을 이해할 수 없다는 것입니다. 대신 강력하고 일반적인 솔루션을 찾으려고합니다.

아마도 효과가 있지만 나는 그것이 보장된다고 믿지 않습니다. 다음은 ISO C ++ 10/5의 인용문입니다.

기본 클래스 하위 목체는 동일한 유형의 가장 파생 된 객체의 레이아웃과 다른 레이아웃 (3.7)을 가질 수 있습니다.

"실제 세계"에서 이것이 실제로 어떻게 될 수 있는지 알기가 어렵습니다.

편집하다:

결론은 표준이 기본 클래스 서브 버젝트 레이아웃이 동일한 기본 유형의 콘크리트 물체와 다를 수있는 장소의 수를 제한하지 않는다는 것입니다. 결과적으로 Pod-Inness 등과 같이 귀하가 가질 수있는 모든 가정이 기본 클래스 하위 목체에 반드시 사실은 아닙니다.

편집하다:

대안적인 접근법과 행동이 잘 정의 된 접근 방식은 'foo'를 'bar'의 구성원으로 만들고 필요한 곳에 전환 연산자를 제공하는 것입니다.

class bar {
public:    
   int my_bar() { 
       return ret_foo( foo_ ); 
   }

   // 
   // This allows a 'bar' to be used where a 'foo' is expected
   inline operator foo& () {
     return foo_;
   }

private:    
  foo foo_;
};
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top