Est-il possible de classer une structure C en C++ et d'utiliser des pointeurs vers la structure en code C?
Est-il un effet secondaire en faisant ceci:
C code:
struct foo {
int k;
};
int ret_foo(const struct foo* f){
return f.k;
}
C++ code:
class bar : public foo {
int my_bar() {
return ret_foo( (foo)this );
}
};
il y a un extern "C"
autour du code C++ et chaque code est dans sa propre unité de compilation.
est-ce portable à travers les compilateurs?
10 réponses
C'est tout à fait légal. En C++, Les classes et les structures sont des concepts identiques, à l'exception que tous les membres de la structure sont publics par défaut. C'est la seule différence. Donc demander si vous pouvez étendre une struct n'est pas différent de demander si vous pouvez étendre une classe.
il y a une mise en garde ici. Il y a aucune garantie de cohérence de mise en page du compilateur au compilateur. Donc, si vous compilez votre code C avec un compilateur différent que votre code C++, vous pouvez rencontrer des problèmes liés à la mise en page des membres (padding en particulier). Cela peut même se produire lorsqu'on utilise des compilateurs C et c++ du même fournisseur.
je ont avait-il passé avec gcc et g++. J'ai travaillé sur un projet qui utilisait plusieurs grosses structures. Malheureusement, g++ a emballé les structures beaucoup plus lâches que gcc, ce qui a causé des problèmes significatifs de partage d'objets entre le code C et C++. Nous avons finalement dû régler manuellement empaqueter et insérer du rembourrage pour que le code C et c++ traite les structures de la même façon. Notez cependant que ce problème peut se produire indépendamment du sous-classement. En fait, nous n'avons pas sous-classé la structure C dans ce cas.
Je ne recommande certainement pas d'utiliser un tel sous-classement bizarre. Il serait préférable de changer votre conception pour utiliser la composition au lieu de l'héritage. Il suffit de faire un membre
foo* m_pfoo;
dans la classe bar et il fera le même travail.
autre chose que vous pouvez faire est de faire une classe de plus FooWrapper, contenant la structure en elle-même avec la méthode getter correspondante. Ensuite, vous pouvez sous-classe wrapper. De cette façon, le problème avec le destructeur virtuel est allé.
" ne dérivent jamais de classes de béton."- Sutter
"le non-feuille de classes abstraites."- Meyers
c'est tout simplement une erreur de sous-classe des classes sans interface. Vous devriez remanier vos bibliothèques.
techniquement, vous pouvez faire ce que vous voulez, tant que vous n'invoquez pas un comportement non défini par, E. G., suppression d'un pointeur vers la classe dérivée par un pointeur vers son sous-objet de classe de base. Vous n'avez pas même besoin de extern "C"
pour le code C++. Oui, c'est portable. Mais c'est une mauvaise conception.
c'est parfaitement légal, bien que cela puisse prêter à confusion pour d'autres programmeurs.
vous pouvez utiliser l'héritage pour étendre les c-structs avec des méthodes et des constructeurs.
échantillon:
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.
};
étendre des structures pourrait être" plus " mal, mais n'est pas quelque chose que vous êtes interdit de faire.
Wow, c'est diabolique.
est-ce portable à travers les compilateurs?
certainement pas. Considérons ce qui suit:
foo* x = new bar();
delete x;
pour que cela fonctionne, le destructeur de foo doit être virtuel, ce qui n'est clairement pas le cas. Tant que vous n'utilisez pas new
et tant que les objets dérivés n'ont pas de destructeurs personnalisés, vous pouvez être chanceux.
/EDIT: On the d'autre part, si le code est seulement utilisé comme dans la question, l'héritage n'a aucun avantage sur la composition. Suivez simplement les conseils de m_pGladiator.
c'est parfaitement légal, et vous pouvez le voir dans la pratique avec les classes MFC CRect et CPoint. CPoint dérive du POINT (défini dans windef.h), et CRect dérive de RECT. Vous décorez simplement un objet avec des fonctions de membre. Tant que vous n'étendez pas l'objet avec plus de données, tout va bien. En fait, si vous avez une structure c complexe qui est une douleur à Default-initialize, l'étendre avec une classe qui contient un constructeur par défaut est un moyen facile de traiter cette question.
même si vous faites ceci:
foo *pFoo = new bar;
delete pFoo;
alors vous allez bien, puisque votre constructeur et destructeur sont triviaux, et vous n'avez pas alloué de mémoire supplémentaire.
vous n'avez pas non plus à envelopper votre objet C++ avec 'extern' C', puisque vous n'êtes pas en train de passer un c++ type aux fonctions C.
Je ne pense pas que ce soit nécessairement un problème. Le comportement est bien défini, et tant que vous êtes prudent avec les problèmes de vie (ne pas mélanger et faire correspondre les allocations entre le code C++ et C) fera ce que vous voulez. Il devrait être parfaitement portable à travers les compilateurs.
le problème avec les destructeurs est réel, mais s'applique chaque fois que la classe de base destructeur n'est pas Virtuel pas seulement pour les structures C. C'est quelque chose dont vous devez être conscient mais qui n'empêche pas d'utiliser ceci modèle.
il fonctionnera, et de manière portable, mais vous ne pouvez pas utiliser de fonctions virtuelles (qui comprend des destructeurs).
je recommande qu'au lieu de faire ceci vous ayez la barre contenir un Foo.
class Bar
{
private:
Foo mFoo;
};
Je ne comprends pas pourquoi vous ne faites pas simplement ret_foo une méthode de membre. Votre façon actuelle rend votre code terriblement difficile à comprendre. Qu'est-ce qui est si difficile d'utiliser une vraie classe en premier lieu avec une variable membre et des méthodes get/set?
je sais qu'il est possible de classer des structures en C++, mais le danger est que d'autres ne seront pas capables de comprendre ce que vous avez codé parce que c'est si rare que quelqu'un le fasse réellement. J'opterais pour une solution robuste et commune. plutôt.
Il en sera probablement de travail, mais je ne crois pas c'est garanti. Voici une citation D'ISO c++ 10/5:
un sous-objet de classe de base peut avoir une disposition (3.7) différente de celle d'un objet le plus dérivé du même type.
Il est difficile de voir comment, dans le "monde réel" cela pourrait effectivement être le cas.
EDIT:
La bas la limite est que la norme n'a pas limité le nombre d'endroits où la disposition d'un sous-objet d'une classe de base peut être différente d'un objet concret avec le même type de Base. Le résultat est que toutes les hypothèses que vous pouvez avoir, telles que la POD-ness, etc. ne sont pas nécessairement valables pour le sous-sujet de la classe de base.
EDIT:
une approche alternative, et dont le comportement est bien défini, est de faire de " foo "un membre du" bar " et de fournir une opérateur de conversion là où c'est nécessaire.
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_;
};