Jump to content

SUBIECTE NOI
« 1 / 5 »
RSS
Alfa Romeo Stelvio 2.2 jtd

Intrebari srl nou

La multi ani @AndReW99!

Alegere masina £15000 uk
 TVR vrea sa lanseze o platforma d...

Strategie investie pe termen lung...

Modulator FM ptr auto alimentat p...

orange cablu f.o. - internet fara...
 Robinet care comuta traseul

A fost lansata Fedora 40

Samsung S24 plus

Imi iau un Dell? (Vostro vs others)
 Abonati Qobuz?

transport -tren

Platforma electronica de eviden&#...

Cot cu talpa montat stramb in per...
 

Virtual tabele and _vptr

- - - - -
  • Please log in to reply
16 replies to this topic

#1
virgil94

virgil94

    Junior Member

  • Grup: Members
  • Posts: 66
  • Înscris: 08.08.2017
Salut,

Am intrat putin in detalii despre cum functioneaza desturctorii virtuali.
Am citit ce a scris colegul aici si totul pare ok despre modul in care se retin adresele metodelor virtuale (https://www.go4exper...le-vptr-t16544/).

Insa..am un mic exemplu si 2 intrebari:
class Base
{
public:
virtual void function1() { cout << "Base :: function1()\n"; }
virtual void function2() { cout << "Base :: function2()\n"; }
virtual ~Base() { cout << "Base destructor" << endl; }
Base() { cout << "Base constructor" << endl; }
};
class D1 : public Base
{
public:
~D1() { cout << "Desturctor of D1" << endl; }
D1() { cout << "Constructor of D1" << endl; }
virtual void function1() { cout << "D1 :: function1()\n"; }
};
class D2 : public D1
{
public:
~D2() { cout << "Desturctor of D2" << endl; }
D2() { cout << "Constructor of D2" << endl; }
//virtual void function2() { cout << "D2 :: function2()\n"; }
};
class D3 : public D2
{
public:
~D3() { cout << "Desturctor of D3" << endl; }
D3() { cout << "Constructor of D3" << endl; }
virtual void function2() { cout << "D3 :: function2()\n"; }
};

int main()
{
D3 *d = new D3;
Base *b = d;
d->function1();
d->function2();
delete (B);
return (0);
}


Interebari:
  • De ce nu trebuie sa fie virtuali si destructorii din clasele derivate? Daca fac un obiect de tip D3 cum stie sa ajunga la D2, D1 fara ca acesita sa fie virtuali?

  • De ce nu reuseste sa fie invovat destructorul lui D3 daca destructorul din clasa de baza nu este virtual ?  In mod normal in exemplul de mai sus Base* b poniteaza spre vTabele a lui D3, in D3 (cum este si in imaginea din link-ul de mai sus) exista destructorul sau salvat, asadar de ce nu reuseste sa-l invoce?


#2
virgil94

virgil94

    Junior Member

  • Grup: Members
  • Posts: 66
  • Înscris: 08.08.2017
Am mai experimentat si am observat ca e suficient ca destructorul virtual sa fie de acelasi "rang" cu cel al clasei pe care lucrez...adica daca am ceva de genul acesta:
D3* d3 = new D3;
D2* ptrd2 = d3;
d3->function1();
delete ptrd2;


Este suficient ca destructorul din D2 sa fie virtual (sters cel din clasa de baza) si functioneaza ok, sau daca il declar in D1 o sa functioneze cu toate subclasele acestuia (D2,D3).  In exemplul de fata D1 este clasa de baza pentru D2, si al fel D2 este clasa de baza pentru D1?

#3
dani.user

dani.user

    Guru Member

  • Grup: Senior Members
  • Posts: 30,235
  • Înscris: 24.02.2007
Mostenire, fara virtual => compilatorul genereaza direct apelul catre Base::foo() https://godbolt.org/g/3YkNJV

Mostenire, cu virtual => compilatorul genereaza cod care se uita prin virtual table https://godbolt.org/g/jqRRLV

Edited by dani.user, 15 January 2018 - 17:25.


#4
virgil94

virgil94

    Junior Member

  • Grup: Members
  • Posts: 66
  • Înscris: 08.08.2017
Am mai aprofundat de cand am postat si (m-am trezit) si am vazut ca o data ce un destructor e declarat virtual in clasa de baza, toate clasele derivate vor avea destructorul virtual.
Ce nu prea inteleg e de unde stie pe care sa-l cheme si de ce trebuie sa fie virtuali?
Si inca o mica nelamurire de care nu am reusit sa dau pe net...atunci cand se deriveaza clasa de baza iar clasa derivata mosteneste vPtr, in pointer raman datele din clasa de baza unde se adauga detaliile clasei derivate sau vPtr va contine detalii doar despre clasa in cauza ?

Edited by virgil94, 15 January 2018 - 17:35.


#5
dani.user

dani.user

    Guru Member

  • Grup: Senior Members
  • Posts: 30,235
  • Înscris: 24.02.2007
vPtr e un array de pointeri la functii (e adaugat automat ca un membru al clasei). In cazul clasei de baza, array-ul va tinti spre functii ale clasei de baza; in cazul celei derivate va tinti spre functii ale clasei derivate.

Un exemplu ce foloseste C: https://forum.softpe...-c-pur-windows/

#6
virgil94

virgil94

    Junior Member

  • Grup: Members
  • Posts: 66
  • Înscris: 08.08.2017

View Postdani.user, on 15 ianuarie 2018 - 17:39, said:

vPtr e un array de pointeri la functii (e adaugat automat ca un membru al clasei). In cazul clasei de baza, array-ul va tinti spre functii ale clasei de baza; in cazul celei derivate va tinti spre functii ale clasei derivate.

Un exemplu ce foloseste C: https://forum.softpe...-c-pur-windows/

Da..dar din ce am citit referindu-ne la clasa derivata aceasta tine pointer la functie catre clasa de baza in cazul in care clasa derivata nu reimplementeaza metoda virtuala definita in clasa de baza.
(https://www.go4exper...le-vptr-t16544/)
Si tot baiatul ala de mai sus spunea va vptr e mostenit (dupa cum a "mazgalit" si pe schema) iar o data ce e mistenit ma gandeam ca e mostenit cu pointerii la functii pr care ii detine clasa de baza.

#7
dani.user

dani.user

    Guru Member

  • Grup: Senior Members
  • Posts: 30,235
  • Înscris: 24.02.2007
Vezi in debugger exact ce contine

#8
virgil94

virgil94

    Junior Member

  • Grup: Members
  • Posts: 66
  • Înscris: 08.08.2017
Deja realitatea bate teoria.

Exemplul de la tine
[color=#ff0000][b]vtable for Derived:[/b][/color]
  .quad 0
  .quad typeinfo for Derived
  .quad Derived::foo()
typeinfo for Derived:
  .quad vtable for __cxxabiv1::__si_class_type_info+16
  .quad typeinfo name for Derived
  .quad typeinfo for Base
typeinfo name for Derived:
  .string "7Derived"
typeinfo for Base:
  .quad vtable for __cxxabiv1::__class_type_info+16
  .quad typeinfo name for Base
typeinfo name for Base:
  .string "4Base"

Nu este nici un vtable pentru base class.

#9
dani.user

dani.user

    Guru Member

  • Grup: Senior Members
  • Posts: 30,235
  • Înscris: 24.02.2007
Nu s-a mai obosit sa-l genereze ca nu era apelat foo din Base niciunde un acel cod.

Un exemplu mai sugestiv: https://godbolt.org/g/dhmwui

#10
virgil94

virgil94

    Junior Member

  • Grup: Members
  • Posts: 66
  • Înscris: 08.08.2017
Golanas si compilatorul asta :)).
Deci din ce vad fiecare e cu vPtr al sau. Atunci de ce mai spunea ala ca se mosteneste?

#11
virgil94

virgil94

    Junior Member

  • Grup: Members
  • Posts: 66
  • Înscris: 08.08.2017
Mai am o mica problema.
#include <iostream>
using namespace std;
class a {
public:
virtual void test() {}
};
class b {
public:
virtual void test2() {}
};
class c : public a, public b
{
public:
virtual void test33() {}
};
int main()
{
a A;
b B;
c C;
A.test();
B.test2();
C.test33();
cout << sizeof(c);
return 0;
}


Daca ma uit cu explore compiler genereaza 3 tabele virtuale, dar in size-ul lui "c" regasesc doar 2, a lui a si b adica 8 bytes.
De ce nu este luata in considerare si vptr pentru C in size-ul total?

#12
dani.user

dani.user

    Guru Member

  • Grup: Senior Members
  • Posts: 30,235
  • Înscris: 24.02.2007
C contine 2 vptr (2*8 = 16 bytes) fiindca sunt doua "ramuri" de mostenire. N-are de ce sa includa 3 fiindca metodele ce le-ai adaugat lui C le poate usor adauga in continuarea uneia dintre ramuri (a ales A in cazul asta)

Daca faci un cast la pointeri, brusc observi ca se schimba valoarea pointerului !

#include <iostream>

class A {
public:
	virtual void test() {}
};

class B {
public:
	virtual void test2() {}
};

class C : public A, public B {
public:
	virtual void test33() {}
};

int main()
{
	A a;
	B b;
	C c;
	a.test();
	b.test2();
	c.test33();

	std::cout << "C e la adresa " << &c << '\n';
	std::cout << "C interpretat ca A ar fi la adresa " << static_cast<A*>(&c) << '\n';
	std::cout << "C interpretat ca B ar fi la adresa " << static_cast<B*>(&c) << '\n';

	std::cout << sizeof(c);
	return 0;
}


Quote

C e la adresa 0x23fe30
C interpretat ca A ar fi la adresa 0x23fe30
C interpretat ca B ar fi la adresa 0x23fe38

Prima parte din vptr-ul lui C poate fi interpretata ca si vptrul lui A, pe a 3-a pozitie gasind pointerul spre test().

#13
TheOriginals

TheOriginals

    Junior Member

  • Grup: Members
  • Posts: 51
  • Înscris: 30.05.2016
Ok, hai sa te mai chinui putin si sa-ti spun ce am inteles eu despre vptr (poate mai ajuta pe cineva).
  • Fiecare clasa are propriul "virtual table" daca are una sau mai multe functii virtuale definite (asta daca nu decide compilatorul sa faca optimizari).
  • vptr este pointer ce indentifica o tabela virtuala
  • tabela virtuala contine pointeri la functii care fac posibila apelarea corecta a metodelor in functie de obiectul instantiat
  • o data creeata o tabela virtuala pentru o clasa aceasta ramane neschimbata (clasele derivate cel mult vor adauga noi metode in functie de optimizarile compilatorului) si mostenita mai departe.
Totusi sunt putin curios cum reuseste sa cheme destructorul corect, daca am:
Base*ptrBase = new Dev;

Destructorul din clasa de baza e virtual, deci automat si cel din clasa derivata.
Inainte sa distruga obiectul Base compilatorul verifica toate tabelel virtuale in cautarea destructorului? Sau deja stie tipul obiectului si verifica direct in tabela acestuia?
Iar daca nu ar fi virtuala, daca nu ar fi in tabela respectiva ce l-ar opri din a-l chema o data ce i-a identificat tipul? Posted Image

Ori destructorii, indiferent de numele lor (fie ~Base, fie ~Dev,) sunt vazuti ca si metode overloaded iar daca in clasa mea derivata nu este supraincarcat destructorul o sa-l cheme direct pe cel din clasa de baza sau urmatorul in ordina inversa creeari?

#14
dani.user

dani.user

    Guru Member

  • Grup: Senior Members
  • Posts: 30,235
  • Înscris: 24.02.2007
Si destructorul virtual ajunge tot in virtual table: https://godbolt.org/g/beKxBa

Se vede ca, chiar fara sa declar destructorul lui C, compilatorul tot il genereaza, si are grija ca acesta sa cheme destructorii lui A si B.

#15
virgil94

virgil94

    Junior Member

  • Grup: Members
  • Posts: 66
  • Înscris: 08.08.2017
Mult mai clar, totusi mai am 2 mici intrebari ca sa-mi inchei ucenicia in vtabele.
  • De ce in vtable pentru A, B si C destructorii lor sunt multiplicati (pentru A : A::~A , A::~A, acelasi lucru se intampla pentru B si C.
  • De ce se mostenesc vptr in clasele derivate daca fiecare clasa isi creeaza prorpriul virtual table unde retin tot ce au nevoie referitor la ierarhia de clase?
Referitor la intrebarea 2 am imaginea asta:
[ https://image.ibb.co/cj8jGm/Capture.png - Pentru incarcare in pagina (embed) Click aici ]
Code pentru imaginea de mai sus pentru cei interesati.
Spoiler

Deci daca sa zic ca sunt cea mai derivata clasa din ierarhia mea in cazul in care nu am supraincarcat o metoda virtuala ma duc si tin un pointer la functie din clasa mai de sus. Adica dupa ce o sa-mi termin treaba in vtable o sa am mapata toata ierarhia de clase astfel incat sa stiu ce functie pot invoca utilizand vtable din clasa mea derivata fara a mai avea nevoie de celelalte tabele.
Deci daca am toate informatiile care imi trebuie in vtable al clasei mele de ce as mai vrea si vtable ale claselor mai mari decat mine in ierarhie (de ce sunt transmise vtable claselor derivate daca teoretic acestea nu le folosesc )?

#16
dani.user

dani.user

    Guru Member

  • Grup: Senior Members
  • Posts: 30,235
  • Înscris: 24.02.2007
Vezi in standard care sunt cerintele pentru vtable. Fiecare compilator e liber sa le implementeze cum doreste.

Personal sunt mai interesat de cum pot evita mostenirea (pentru performanta sporita) decat de fiecare amanunt al implementarii sale.

#17
virgil94

virgil94

    Junior Member

  • Grup: Members
  • Posts: 66
  • Înscris: 08.08.2017
Da, nu stiu ce m-a lovit asa tare. Ma gandeam ca pentru a evita mostenira lui vptr e de ajuns final dar se pare ca nu e chiar asa. Am sa ma mai interesez. Mersi de hint.:)

Anunturi

Chirurgia endoscopică a hipofizei Chirurgia endoscopică a hipofizei

"Standardul de aur" în chirurgia hipofizară îl reprezintă endoscopia transnazală transsfenoidală.

Echipa NeuroHope este antrenată în unul din cele mai mari centre de chirurgie a hipofizei din Europa, Spitalul Foch din Paris, centrul în care a fost introdus pentru prima dată endoscopul în chirurgia transnazală a hipofizei, de către neurochirurgul francez Guiot. Pe lângă tumorile cu origine hipofizară, prin tehnicile endoscopice transnazale pot fi abordate numeroase alte patologii neurochirurgicale.

www.neurohope.ro

0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

Forumul Softpedia foloseste "cookies" pentru a imbunatati experienta utilizatorilor Accept
Pentru detalii si optiuni legate de cookies si datele personale, consultati Politica de utilizare cookies si Politica de confidentialitate