Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 05/01/2010, à 23:35

Khyl

[Résolu] Colle en C++

Cet après-midi, j'étais en train d'expliquer à un collègue le B-A-BA du C++ et j'ai fait ci-dessous un programme de base tout ce qu'il y a de plus banal. Il me demande si on peut faire un sizeof comme en C, je dis oui et là je suis tombé sur une chose que je ne comprends pas :

#include <iostream>

class animal {};
class loup : public virtual animal {};
class homme : public virtual animal {};
class loupgarou : public loup, public homme {};

int main()
{
	std::cout << sizeof(loupgarou) << std::endl;
	return 0;
}

En retour, il me rend 8 !!! roll
Je m'attendais à 0 en retour étant donné que je n'ai rien instancié, pour moi ce n'est que du déclaratif. Du coup, je suis incapable d'expliquer pourquoi il me sort 8 octets. Auriez-vous une idée ?

Dernière modification par Khyl (Le 06/01/2010, à 00:15)

Hors ligne

#2 Le 05/01/2010, à 23:45

grim7reaper

Re : [Résolu] Colle en C++

Je suis novice en C++ donc je ne suis pas sûr de ce que j'avance mais vu que ce sont des classes virtuelles ça ne serait pas dû à (du moins en partie) la taille de la vtbl ?

Dernière modification par grim7reaper (Le 05/01/2010, à 23:53)

Hors ligne

#3 Le 05/01/2010, à 23:50

Khyl

Re : [Résolu] Colle en C++

Possible que ça soit ça, je viens de pousser un peu le bouchon en faisant d'autres sizeof du coup et je reste complètement surpris

#include <iostream>

class animal {};
class loup : public virtual animal {};
class homme : public virtual animal {};
class loupgarou : public loup, public homme {};

int main()
{
	std::cout << sizeof(animal) << std::endl;
	std::cout << sizeof(loup) << std::endl;
	std::cout << sizeof(homme) << std::endl;
	std::cout << sizeof(loupgarou) << std::endl;
	return 0;
}

En retour, j'ai 1, 4, 4 et 8 octets. Evidemment, ce n'est pas dramatique si je n'explique pas cela mais c'est pour ma culture personnelle, j'aime bien comprendre les choses et là je sens qu'il y a un truc que je n'ai pas saisi.

Dernière modification par Khyl (Le 05/01/2010, à 23:51)

Hors ligne

#4 Le 05/01/2010, à 23:53

obiwankennedy

Re : [Résolu] Colle en C++

Ton code (du premier post) sur un système 64 bits me donne 16. Si je supprime le virtual , j'ai 2.

Dernière modification par obiwankennedy (Le 05/01/2010, à 23:55)


Dans mes logiciels, j'écris ton nom.
SGNGD: SvgGd is Not GD
Rolisteam

Hors ligne

#5 Le 05/01/2010, à 23:55

grim7reaper

Re : [Résolu] Colle en C++

Déjà de base un sizeof qui renvoie 0 est impossible. En effet, tout chose occupe de la mémoire (sinon c'est impossible à adresser, via pointeur entre autres) et par conséquent doit avoir une taille (au minimum 1 donc, mais ça doit varier selon les compilateurs et les contraintes d'alignements). Ce qui explique le 1 de la classe animal.

Dernière modification par grim7reaper (Le 06/01/2010, à 00:00)

Hors ligne

#6 Le 05/01/2010, à 23:56

Khyl

Re : [Résolu] Colle en C++

Je suis sur un système 32 bits donc ça explique pourquoi tu as 16 et moi 8.

Dernière modification par Khyl (Le 05/01/2010, à 23:56)

Hors ligne

#7 Le 05/01/2010, à 23:57

telliam

Re : [Résolu] Colle en C++

sizeof donne la taille de la classe tu n'as pas besoin d'instancier un objet.
il te donne 8 car un objet de la classe loupgarou, contient une référence vers la table virtuel ( 4 octets)  de la classe homme et une référence vers la table virtuel de la classe loup ( 4 octets)
plus d'info : http://www.phpcompiler.org/articles/vir … tance.html


"- Un intellectuel assis va moins loin qu'un con qui marche."
Maurice Biraud - Un Taxi pour Tobrouk
Michel Audiard

Hors ligne

#8 Le 05/01/2010, à 23:59

obiwankennedy

Re : [Résolu] Colle en C++

Visiblement, si je crée une instance d'une loupgarou. ça fait la même taille que la classe elle même.


Dans mes logiciels, j'écris ton nom.
SGNGD: SvgGd is Not GD
Rolisteam

Hors ligne

#9 Le 06/01/2010, à 00:03

telliam

Re : [Résolu] Colle en C++

c'est tout a fait normal,
sizeof renvoie la taille d'un type ou du type de la variable passée en parametre.

Dernière modification par telliam (Le 06/01/2010, à 00:08)


"- Un intellectuel assis va moins loin qu'un con qui marche."
Maurice Biraud - Un Taxi pour Tobrouk
Michel Audiard

Hors ligne

#10 Le 06/01/2010, à 00:14

Khyl

Re : [Résolu] Colle en C++

Merci !!! grâce à vous, j'ai compris ce qu'il se passe big_smile

La classe animal a sûrement un coût d'un seul octet dans le but de pouvoir obtenir plusieurs instances de la même classe.

Les classes virtuelles dérivées loup et homme ont un coût en octets (pointeur vers une table d'adressage du sous objet de chaque classe virtuelle) : 4 octets

Pour loupgarou, c'est sûrement le coût total des classes homme + loup donc 8 octets, sachant que la classe loupgarou elle-même fait sans doute 0 octet

Ca explique bien les choses, merci, on peut dire que c'est résolu, je ne vois pas mieux que vos explications et que la documentation de telliam

Dernière modification par Khyl (Le 06/01/2010, à 00:26)

Hors ligne

#11 Le 07/01/2010, à 18:08

cpp_dev

Re : [Résolu] Colle en C++

Khyl a écrit :

Merci !!! grâce à vous, j'ai compris ce qu'il se passe big_smile

La classe animal a sûrement un coût d'un seul octet dans le but de pouvoir obtenir plusieurs instances de la même classe.

Les classes virtuelles dérivées loup et homme ont un coût en octets (pointeur vers une table d'adressage du sous objet de chaque classe virtuelle) : 4 octets

Pour loupgarou, c'est sûrement le coût total des classes homme + loup donc 8 octets, sachant que la classe loupgarou elle-même fait sans doute 0 octet

Ca explique bien les choses, merci, on peut dire que c'est résolu, je ne vois pas mieux que vos explications et que la documentation de telliam

Bonjour,
Tous les objets en C++ doivent avoir une taille >= 1. Le langage impose cela dans la norme. Ne serait-ce que pour pouvoir faire des tableaux dont les éléments ont des adresses différentes : animal zoo[12] : il faut que &zoo[0] soit différente de &zoo[1]. C'est pour ça que les classes même si elles n'ont pas de membre ont un sizeof qui vaut au moins 1. Le compilateur est libre de prendre une autre valeur en particulier si on est sur une plateforme avec des problèmes d'alignements.

Les fonctions virtuelles sont appelées à l'exécution selon l'objet effectif sur lequel elle sont invoquées. Pour que tout cela fonctionne, les classes possédant une fonction virtuelle ont une table de fonctions virtuelles et les instances de ces classes un vpointeur vers cette table. Sur une plateforme 32 bits, ce pointeur sera de taille 4, sur une plateforme 64 bits, il sera de 8 si les @ sont sur 64 bits. Ceci est assez bien expliquer dans ce tutoriel : Résoudre l'appel dynamiquement : les tables virtuelles. C'est pour ça que toute les classes contenant au moins une fonction virtuelle auront une taille suffisante pour contenir le vpointeur et ce même si elles n'ont pas d'autres membres :

#include <iostream>

struct non_polymorphic_type
{
};

struct polymorphic_type
{
   virtual ~polymorphic_type();
};


int main()
{
   std::cout<<"sizeof(non_polymorphic_type) = "<<sizeof(non_polymorphic_type)<<"\n";
   std::cout<<"sizeof(polymorphic_type) = "<<sizeof(polymorphic_type)<<"\n";

   return 0;
}

sizeof(non_polymorphic_type) == 1
sizeof(polymorphic_type) == sizeof(void*)==4

Pour l'héritage, on peut distinguer plusieurs cas :
-> Les classes de bases n'ont pas de fonctions virtuelles et l'héritage n'est pas virtuel :

#include <iostream>
class big_base{};
class base_1 : public big_base{};
class base_2: public big_base{};
class derivee : public base_1, public base_2{};

template<class T> void print_size()
{
   std::cout<<sizeof(T)<<std::endl;
}

int main()
{
   print_size<big_base>();
   print_size<base_1>();
   print_size<base_2>();
   print_size<derivee>();
    return 0;
}

Les compilateurs optimisent ce cas et toutes ces classes ont la taille minimal (ici 1). Notes que chez moi, derivee a une taille de 2 avec gcc et une taille de 1 avec Visual sous Windows. Les compilateurs se comportent différemment (c'est bien une question de compilateur et non de OS). Le langage n'impose pas un comportement par défaut. Le seul comportement imposé est le même que précédemment : sizeof(type)>=1. Pourquoi sous gcc, derivee a une taille de 2 ? Je n'en n'ai aucune idée et je ne vois pas de raison. Peut être quelqu'un a-t-il une explication ?

-> Les classes de bases ont au - une fonction virtuelle et l'héritage n'est pas virtuel :

#include <iostream>
class big_base
{
public :
   virtual ~big_base(){}
};
class base_1 : public big_base{};
class base_2: public big_base{};
class derivee : public base_1, public base_2{};

template<class T> void print_size()
{
   std::cout<<sizeof(T)<<std::endl;
}

int main()
{
   print_size<big_base>();
   print_size<base_1>();
   print_size<base_2>();
   print_size<derivee>();
    return 0;
}

big_base, base_1 et base_2 ont besoin d'un vpointeur et donc une taille permettant le stockage de ce vpointeur (ici, 4) (je suis sur une plateforme 32 bits).
derivee possède deux branches d'héritage distinctes et a donc besoin de 2 vpointeurs : un pour base_1/big_base et un pour base_2/big_base. Sa taille est donc telle qu'il puisse stocker ces 2 pointeurs (ici, 8).

-> L'héritage est virtuel et pas de fonction virtuelle :
(note : Héritage virtuel et fonction virtuelle utilisent tous 2 le mot clé virtual mais cela n'a rien à voir.)

#include <iostream>
class big_base{};
class base_1 : virtual public big_base{};
class base_2: virtual public big_base{};
class derivee : public base_1, public base_2{};

template<class T> void print_size()
{
   std::cout<<sizeof(T)<<std::endl;
}

int main()
{
   print_size<big_base>();
   print_size<base_1>();
   print_size<base_2>();
   print_size<derivee>();
    return 0;
}

big_base a une taille de 1.
base_1 a une taille de 4 et base_2 aussi !
derivee a une taille de 8 !
Mais pourquoi donc puisqu'il n'y a pas de fonctions virtuelles ?
L'héritage virtuel introduit 4 octets supplémentaires pour pouvoir revenir à la classe big_base correctement à partir d'un pointeur base_1 ou base_2 sur un objet de type derivee. Si tu parles l'anglais, la meilleur explication est donnée ici : Memory Layout for Multiple and Virtual Inheritance
En gros, on a encore un vpointer mais non plus sur une table de fonctions virtuelles mais sur un offset indiquant comment revenir à la classe de base (big_base) selon le type effectif de l'objet (un derivee ou un base_X).
base_1 et base_2 ont donc besoin de stocker un vpointeur et ont donc une taille de 4. derivee a une taille de 8, une pour chacun de ces vpointer.

-> L'héritage est virtuel et il y a au moins une fonction virtuelle :

#include <iostream>
class big_base
{
   public :
   virtual ~big_base(){}
};
class base_1 : virtual public big_base{};
class base_2: virtual public big_base{};
class derivee : public base_1, public base_2{};

template<class T> void print_size()
{
   std::cout<<sizeof(T)<<std::endl;
}

int main()
{
   print_size<big_base>();
   print_size<base_1>();
   print_size<base_2>();
   print_size<derivee>();
    return 0;
}

gcc donne :4 pour big_base, base_1 et base_2 et 8 pour derivee.
4 pour big_base car big_base contient une fonction virtuelle et a donc besoin d'un vpointeur sur une vtable.
4 pour base_1 et base_2. Le vpointeur pointe sur une structure contenant à la fois l'offset lié à l'héritage virtuel et à la fois les infos de la vtable.
8 pour derivee, 4 pour la branche passant par base_1 et 4 pour la branche passant par base_2.
Ce qui est intéressant c'est que Visual sous windows donne un résultat différent : 4 pour big_base, 8 pour base_1 et base_2 et 12 pour derivee. Visual gère les deux vpointeurs (vtable et offset) séparément. big_base a une taille de 4 pour le pointeur sur la vtable. base_1 et base_2 ont une taille de 8 car ils ont 2 pointeurs : un pour la vtable et un pour l'offset. derivee a une taille de 12 : 4 pour le pointeur sur la vtable (les données de big_base sont partagées avec l'héritage virtuel), 4 pour le vpointeur sur l'offset de base_1 et 4 pour le vpointeur sur l'offset de base_2.
Voilà.
Je ne sais pas si j'ai été clair.
Ce qui est intéressant de retenir :
-> Une classe n'a jamais une taille de 0
-> La taille d'une classe dépend : de fonctions virtuelles, du type d'héritage MAIS SURTOUT du compilateur.

Hors ligne

#12 Le 07/01/2010, à 23:55

Khyl

Re : [Résolu] Colle en C++

Je te remercie cpp_dev pour avoir pris beaucoup de temps à tout bien expliquer, c'est parfaitement clair et tes exemples sont simples et concrets. Pour le lien en anglais, c'est le même que celui que Telliam m'avait donné un peu plus haut big_smile je l'avais lu un peu avant héhé

J'espère que je pourrais t'aider à mon tour sur un autre sujet, ce soir j'ai pris un cours wink

Hors ligne