2013-12-05 2 views
5

я вспоминаю я видел где-то какой-то код, который используется, чтобы иметь-структуру в качестве базового класса и класса C++ в качестве производного классаструктура в качестве базового класса - C/C++ совместимости

struct Base_Struct 
{ 

} 

class Derived : Base_Struct 
{ 
    ... 
} 

И дела что указатель на Base_Struct * был передан из файлов C++ в некоторые файлы C, которым затем удалось использовать некоторые указатели на функции в Base_Struct.

Мой вопрос: если я передам Base_Struct * в файл C, сможет ли код C полностью использовать Base_Struct? Как насчет производного класса?

+0

Мое понимание распределения структур классов заключается в том, что базовый класс является первым, а члены класса dirived сохраняются после базового класса. Это должно сработать. Но я не юрист по языку, поэтому не верьте мне на слово. Массив структур может не работать, поскольку они будут больше, чем Base_Struct. –

ответ

3

Если я прохожу Base_Struct* в файл C, будет код C иметь возможность использовать Base_Struct полностью?

Если это класс стандартного макета, а компилятор C использует тот же ABI для таких классов, как компилятор C++, то он может обращаться ко всем членам данных. Очевидно, что он не мог получить доступ к каким-либо элементам-членам или статическим членам, поскольку такие вещи не существуют в C и должны быть исключены из любого C-совместимого определения структуры.

Что относительно производного класса?

Вы не можете определить этот класс в программе на C, чтобы он не мог ничего сделать с помощью указателя.

+0

Где «класс стандартного макета» означает [Обычные старые данные] (https://en.wikipedia.org/wiki/Plain_old_data), то есть что-то, что может быть скомпилировано компилятором C. Если он использует любые функции C++ (частные/защищенные члены, виртуальные функции или родительский класс/структура), это не POD. –

+4

@AdamRosenfield: Нет, класс стандартного макета означает «класс стандартного макета». POD - это особый случай стандартной компоновки с дополнительными ограничениями. –

+0

Основная причина (по крайней мере, для меня) расширить структуры из C API - дать им конструктор по умолчанию, который будет обнулять их поля и, возможно, деструктор ... –

1

Возможно, он может работать для базового класса. Но это на очень частный случай:

  • POD структура
  • же упаковка
  • же во время выполнения, если вы выделяете/DEALLOCATE в C и C++
  • чистый синтаксис C
  • и т.д ...

Но это будет никогда непосредственно для производного класса.

Но создайте так много ограничений, что я бы предоставил API уровня C, чтобы выделить манипуляторы для этих указателей, определенных как псевдоним на «void *» (я даже не уверен, что вы можете делать такие псевдонимы в чистом C).

Что-то вроде:

typedef void* BaseStructPtr; 

BaseStructPtr AllocateBase(/* constructor params */); 
BaseStructPtr AllocateDerived(/* constructor params */); 

TypeOfField1 GetField1(BaseStructPtr ptr); 
TypeOfField2 GetField2(BaseStructPtr ptr); 

/* etc... */ 
1

Я не вижу никаких проблем вывести из структуры, объявленной в C:

C/C++

#include <stdio.h> 

#ifdef __cplusplus 
extern "C" { 
#endif 

typedef struct { 
    int a; 
    int b; 
} X; 

void print(struct StructX* x) { 
    printf("%d, %d\n", x->a, x->b); 
} 

#ifdef __cplusplus 
} // extern "C" 
#endif 

C++

#include <iostream> 

struct Y : StructX { 
    int c; 

    Y() 
    : c(3) 
    { 
     a = 1; 
     b = 2; 
    } 

    void print() { 
     ::print(this); 
     std::cout << c << std::endl; 
    } 
}; 


int main() { 
    Y y; 
    y.print(); 
} 

Имея структуру, объявленную в C++ усложнит ситуацию. Теперь структура может (или будет) иметь информацию, о которой не известно и (скажем) memcpy может ввести неопределенное поведение. Чтобы избежать этого, вы должны обернуть функциональные структуры C++ в C:

C/C++

#ifdef __cplusplus 
extern "C" { 
#endif 

typedef struct Z; // An opaque type (for X) to ensure minimum type safety. 

extern int a(Z*); 
extern int b(Z*); 

#ifdef __cplusplus 
} // extern "C" 
#endif 

C++

extern "C" { 
    int a(Z* z) { 
     return ((X*)z)->a; 
    } 
    int b(Z* z) { 
     return ((X*)z)->b; 
    } 
} // extern "C" 
1

В вашем образце да в C компилятор сможет использовать указатель Base_Struct * и элементы доступа. Вы можете использовать одно и то же определение структуры в файлах C и CPP без проблем. Пока это Cpp, который создает структуру для файла C, компилятор выполнит эту работу.

void Give_Typed_Pointer_to_C_Function(Base_Struct* pStruct) {...} 
void Give_Pointer_to_C_Function(void* pStruct) {...} 
Base_Struct A; 
Derived B; 
Give_Typed_Pointer_to_C_Function(&A);   // OK 
Give_Typed_Pointer_to_C_Function(&B);   // OK 
Give_Pointer_to_C_Function(&A);    // OK  
Give_Pointer_to_C_Function(&B);    // wrong 
Give_Pointer_to_C_Function((Base_Struct*)&B); // OK 

Для этой работы вы должны использовать один и то же выравнивание и упаковку структуры, но, как вы передаете указатель Я полагаю, вы же проект так и должно быть уже так.

Вы даже можете сделать больше, если вы держите структуры в том порядке,

struct S1 { int x; 
      int y; 
      }; 
class C1 { int x; 
      int y; 
      int add(int,int); 
      }; 
void Give_Typed_Pointer_to_C_Function(S1 *pStruct); 
C1 object; 
Give_Typed_Pointer_to_C_Function((S1*)&object); // OK 

// an safer alternative as you can't have to be sure that S1 and C1 match 
struct C1 { int x; 
      int y; 
#ifdef __cplusplus 
      int add(int,int); 
#endif 
      }; 

Если поставить CPP ключевое слово как класс/государственные/частные и методы структуры с Определяешь. И если вы не используете наследование, это будет работать.

Смежные вопросы