Est-il possible de sous-classer une structure C en C ++ et d'utiliser des pointeurs sur la structure en code C?

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

  •  02-07-2019
  •  | 
  •  

Question

Existe-t-il un effet secondaire?

Code C:

struct foo {
      int k;
};

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

Code C ++:

class bar : public foo {

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

};

Il existe un extern "C" autour du code C ++ et chaque code est contenu dans sa propre unité de compilation.

Est-ce portable entre les compilateurs?

Était-ce utile?

La solution

Ceci est tout à fait légal. En C ++, les classes et les structures sont des concepts identiques, à l'exception du fait 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 structure n’est pas différent de demander si vous pouvez étendre une classe.

Il y a une mise en garde ici. Aucune garantie sur la cohérence de la présentation d'un compilateur à l'autre. Ainsi, si vous compilez votre code C avec un compilateur différent de votre code C ++, vous risquez de rencontrer des problèmes liés à la disposition des membres (notamment le remplissage). Cela peut même se produire lorsque vous utilisez des compilateurs C et C ++ du même fournisseur.

J'ai eu cela est arrivé avec gcc et g ++. J'ai travaillé sur un projet qui utilisait plusieurs grandes structures. Malheureusement, g ++ a rendu les structures beaucoup plus lâches que gcc, ce qui a posé de gros problèmes de partage des objets entre le code C et C ++. Nous avons finalement dû configurer manuellement et insérer un remplissage pour que les codes C et C ++ traitent les structures de la même manière. Notez cependant que ce problème peut se produire indépendamment du sous-classement. En fait, nous ne sous-classions pas la structure C dans ce cas.

Autres conseils

Je ne recommanderais certainement pas d’utiliser un sous-classement aussi étrange. Il serait préférable de changer votre conception pour utiliser la composition plutôt que l'héritage. Il suffit de faire un membre

  

foo * m_pfoo;

dans la classe de barre et il fera le même travail.

Vous pouvez également créer une classe FooWrapper supplémentaire, contenant la structure en elle-même avec la méthode getter correspondante. Ensuite, vous pouvez sous-classer le wrapper. De cette façon, le problème avec le destructeur virtuel a disparu.

  

“Ne dérivez jamais de classes concrètes.” - Sutter

     

“Rendre abstraites les classes non-feuilles.” - Meyers

Il est tout simplement faux de sous-classer les classes non-interfaces. Vous devriez refactoriser vos bibliothèques.

Techniquement, vous pouvez faire ce que vous voulez, à condition de ne pas invoquer un comportement indéfini, par exemple. g., supprimer un pointeur sur la classe dérivée par un pointeur sur son sous-objet de classe de base. Vous n'avez même pas besoin de extern " "C" pour le code C ++. Oui, c’est portable. Mais sa conception est médiocre.

Ceci est parfaitement légal, bien que cela puisse être déroutant pour les autres programmeurs.

Vous pouvez utiliser l'héritage pour étendre les structures C avec des méthodes et des constructeurs.

Exemple:

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.
};

L'extension des structures peut être "plus". mal, mais ce n’est pas quelque chose qui vous est interdit.

Wow, c'est le mal.

  

Est-ce portable entre les compilateurs?

Très certainement pas. Considérez 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 que l'objet object dérivé n'a pas de destructeurs personnalisés, vous pouvez être chanceux.

/ EDIT: Par contre, si le code est uniquement utilisé comme dans la question, l'héritage n'a aucun avantage sur la composition. Suivez simplement les conseils donnés par m_pGladiator.

Ceci est parfaitement légal et vous pouvez le voir en pratique avec les classes MFC CRect et CPoint. CPoint dérive de POINT (défini dans windef.h) et CRect dérive de RECT. Vous décorez simplement un objet avec des fonctions membres. Tant que vous n'allongez pas l'objet avec plus de données, tout va bien. En fait, si vous avez une structure C complexe qui est difficile à initialiser par défaut, son extension avec une classe contenant un constructeur par défaut est un moyen simple de résoudre ce problème.

Même si vous faites cela:

foo *pFoo = new bar;
delete pFoo;

alors ça va, car votre constructeur et votre destructeur sont triviaux, et vous n'avez alloué aucune mémoire supplémentaire.

Vous n'avez pas non plus à encapsuler votre objet C ++ avec "extern", car vous ne transmettez pas réellement un type C ++ 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 durée de vie (ne mélangez pas et ne faites pas correspondre les allocations entre le code C ++ et le code C), vous ferez ce que vous voulez. Il devrait être parfaitement portable entre les compilateurs.

Le problème avec les destructeurs est réel, mais s'applique à tout moment où le destructeur de classe de base 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 l’utilisation de ce modèle.

Cela fonctionnera, et de manière portable MAIS vous ne pouvez utiliser aucune fonction virtuelle (y compris les destructeurs).

Je recommanderais plutôt que Bar ait un Foo au lieu de le faire.

class Bar
{
private:
   Foo  mFoo;
};

Je ne comprends pas pourquoi vous ne faites pas simplement ret_foo une méthode membre. Votre manière actuelle rend votre code extrêmement difficile à comprendre. Pourquoi est-il 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 sous-classer des structures en C ++, mais le danger est que d’autres ne puissent pas comprendre ce que vous avez codé, car il est si rare que quelqu'un le fasse réellement. Je choisirais plutôt une solution robuste et commune.

Cela fonctionnera probablement, mais je ne pense pas que cela soit garanti. Voici une citation d'ISO C ++ 10/5:

  

Un sous-objet de classe de base peut avoir une présentation (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 ligne du bas est que la norme n’a pas limité le nombre d’endroits où une présentation de sous-objet de classe de base peut être différente d’un objet concret ayant le même type de base. Le résultat est que toutes les hypothèses que vous pouvez avoir, telles que POD-ness, etc. ne sont pas nécessairement vraies pour le sous-objet de la classe de base.

EDIT:

Une autre approche, dont le comportement est bien défini, consiste à faire de "foo" un membre de "bar" et à fournir un opérateur de conversion si 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_;
};
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top