Виртуалды кесте көрсеткішін (C ++-де) объект үшін дәл белгіленеді ме?

Виртуалды функцияны немесе класстан алынған виртуалды функцияны қамтитын сыныпқа ие кез келген сынып үшін компилятор екі нәрсені істейді. Біріншіден, бұл сынып үшін виртуалды кесте жасайды және екіншіден, ол объект үшін негізгі бөлікке виртуалды көрсеткішті (vptr) қояды. Орындалу кезінде бұл vptr тағайындалады және объектінің жасалуы кезінде дұрыс vtable нұсқауын көрсетеді.

Менің сұрағым мынада, бұл дәл осы процесте қайда пайда болады? Vptr тапсырмасы конструктор алдында/кейін объектінің конструкторында бола ма?

8
Техникалық тұрғыдан, ол конструктор орындалмай тұрып, меңзерді орнатуы керек, себебі нысан қазірдің өзінде қазірдің өзінде нысанның нысаны (бірақ инициализацияланған емес). Бірақ қайтадан стандартты талап етілмейді, бұл тек виртуалды функциялардың ортақ тәсілі болып табылады. Сонымен қатар, конструктор ішіндегі түрі белгілі болғандықтан, конструктордан жасауға болатын виртуалды функционалдық қоңыраулар статикалық түрде шешіледі. Сондықтан, кейіннен vtable-ты инициализациялау мүмкін болады (егер сіз дұрыс болмаса да).
қосылды автор Damon, көзі
@CatPlusPlus қайтадан жасалмайды ... vtable туралы сұрайтын барлық мәселе анықтаманы іске асыруға байланысты. Айқындықты қайта айтуға болмайды.
қосылды автор David Rodríguez - dribeas, көзі
@Als :) Мен жауапты оқыдым (және ұмытып) ... Мен шын мәнінде қандай тілдің және орындаушы екенін білу қызықтырады. Виртуалды жөнелтуді тығыз циклдар үшін vtable inefficient арқылы қарау өте аз адамдар бар және қоңырау шалушының жад мекенжайы бойынша vtable эквивалентін іздеу, әрине, төмендегідей бірнеше рет қымбат болады вptр мен vtr-ге жіберу және сол жерден жіберу ... Мен тек нақты процессордың нұсқауларын ойлап қана қоймай, сондай-ақ деректердің орналасуын (Judy ағашының жаяу серуендеуі бірнеше кэшті жіберіп жіберуі мүмкін ...)
қосылды автор David Rodríguez - dribeas, көзі
@Als: Менің ойымша, компьютерлік ғылымға теориялық тәсілін немесе практикалық біреуін қажет ететініне байланысты. Мен vtable және vptr-дың C ++-ға енгеннен басқа ешқандай баламасы туралы білмеймін, және стандарттағы мандат-тегжейлі нұсқаулықтың қалай жаңартылуы керек екенін анықтайды (бұл динамикалық жіберу үшін таңдау шешімі болса). Ия, ол стандарт бойынша мандатқа ие емес, сонымен бірге барлық енгізулерінің бірдей екенін мағынасында іс жүзінде іске асыру тәуелсіз бұл құрмет
қосылды автор David Rodríguez - dribeas, көзі
@Damon: Шын мәнінде, сіздің негізгі түріңізге сілтеме жасайтын функцияға * this өтуге рұқсат етіледі және сол жерден виртуалды функцияны шақырыңыз. Басқа функцияның ішіндегі жіберілім динамикалық болуы керек, себебі компилятор шалушының кім екенін білмейді.
қосылды автор David Rodríguez - dribeas, көзі
@ DavidRodríguez-dribeas: Ах, сілтемеңізді елемей, жай ғана жауабында кейбір жауаптардан сізден пікірлер көрді.
қосылды автор Alok Save, көзі
@ DavidRodríguez-dribeas: Мен бұл Q жауап беруге лайық екендігімен келісемін және бұл шынымен де жарамды Q, бірақ бұл іске асырудың белгілі бір себебі болып табылады, себебі жаңа пайдаланушылар көп (сенемін, мен де біреу болды) барлық виртуалды механизм механизмін іске асыру тәуелді болып табылады, оның үстіне бағдарламалауға компьютерлік ғылымнан фундамен айналысатындарға байланысты.
қосылды автор Alok Save, көзі
@ DavidRodríguez-dribeas: Мен келісемін, компиляторды ешқашан басқа жолмен жүзеге асыратын компиляторды ешқашан көрген емеспін. Көптен бері маған сол динамикалық диспетчерлік механизмдер туралы ең қызығушылығын тудырдым және Q-да осында сұрадым. Сіз оны бұрын оқыдыңыз, бірақ сіз мазмұнды қызықты таба аласыз. A виртуалды тетігі туралы сұрақ C ++ (Q тақырыбын шынымен жаман деп мойындаймын, бұл менің алғашқы күндерімде SO)
қосылды автор Alok Save, көзі
Бұл толығымен тәуелді.
қосылды автор Cat Plus Plus, көзі

5 жауаптар

Бұл қатаң түрде жүзеге асырылады.

Көптеген компиляторлар үшін,

The compiler initializes this->__vptr within each constructor's Member Initializer list.

Идея әр объектінің v-pointer-ына сыныптың v-кестесінде көрсетуге әкеледі және компилятор бұл үшін жасырын кодты жасайды және конструктор кодын қосады. Бір нәрсе:

Base::Base(...arbitrary params...)
   : __vptr(&Base::__vtable[0])  ← supplied by the compiler, hidden from the programmer
 {

 }

This C++ FAQ explains a gist of what exactly happens.

9
қосылды
Сілтеме енді жұмыс істемейді.
қосылды автор Stav Alfi, көзі
@ DavidRodríguez-dribeas Мен оны жауапкершіліктен бас тартқаны үшін кінәламаймын - жауап бермей, тіпті жасамасаңыз да, тым көп адам жауап бермейді.
қосылды автор Mark Ransom, көзі
Бұл іс-шара vtable-ны пайдалануды таңдау ретінде байланысты. Vtables (яғни all ) қолданатын барлық құрастырушылар үшін құрылыс/жоюдың әрбір деңгейінде vptr-ды жаңартудан басқа таңдау жоқ.
қосылды автор David Rodríguez - dribeas, көзі
@ DavidRodríguez-dribeas: Мен келісемін, vtable , vptr тетігі туралы білетін барлық компиляторлар және мұндай механизмді қолдануы керек, бірақ оны жүзеге асыру міндетті емес Стандарт, демек, алғашқы мәлімдеме.
қосылды автор Alok Save, көзі

Vtable көрсеткіші иерархиядағы әрбір конструкторға кіруде жаңартылып, одан кейін әр деструктордың енгізілуінде қайтадан жаңартылады. Vptr базалық класқа нұсқайды, содан кейін түрлі деңгейлер инициализацияланған кезде жаңартылады.

Көптеген адамдардан бұл іске асырудың белгілі бір түрі бар, бірақ бұл барлық компиляторлар втабликтерді пайдаланады, және де сіз тек қана втаблиді әдісті таңдағаннан кейін, стандартта бұл орындалатын объектінің түрі - орындалатын конструктор/деструктор және бұл, өз кезегінде, динамикалық тарату механизміне қарамастан, құрылыс/қирату тізбегі қиылысатын етіп түзетілуі керек дегенді білдіреді.

Келесі код үзіндісін қарастырыңыз:

#include 

struct base;
void callback( base const & b );
struct base {
   base() { callback( *this ); }
   ~base() { callback( *this ); }
   virtual void f() const { std::cout << "base" << std::endl; }
};
struct derived : base {
   derived() { callback( *this ); }
   ~derived() { callback( *this ); }
   virtual void f() const { std::cout << "derived" << std::endl; }
};
void callback( base const & b ) {
   b.f();
}
int main() {
   derived d;
}

Бұл бағдарламаның шығуы base , derived , derived , base , бірақ қоңырау callback функциясы барлық төрт қоңыраудан бірдей. Жалғыз әдіс - бұл құрылыстың/қираудың жоғарылауы сияқты объектідегі vptr-ды жаңарту арқылы жүзеге асырылады.

8
қосылды

Бұл MSDN мақаласы оны толығырақ түсіндіреді

Онда былай делінген:

«Және соңғы жауап ... сіз күткендей, бұл конструкторда болады.»

Егер мен конструктордың басында, сіздің конструкторыңызда болуы мүмкін кез келген басқа кодты шығармас бұрын, қоссам болады.


But be careful, let's say you have the class A, and a class A1 derived from A.

  • Егер сіз жаңа A нысаны жасасаңыз, vptr A сыныпты конструкторының басында орнатылады
  • Егер сіз A1 жаңа нысан жасасаңыз:

"Here's the entire sequence of events when you construct an instance of class A1:

  1. A1::A1 calls A::A
  2. A::A sets vtable to A's vtable
  3. A::A executes and returns
  4. A1::A1 sets vtable to A1's vtable
  5. A1::A1 executes and returns "
1
қосылды

Конструктордың органында виртуалды функцияларды шақыруға болады, сондықтан егер іске асыру vptr қолданылған болса, бұл vptr .

Ctor-де шақырылған виртуальды функцияларды бұл конструктор класында анықталған, бірақ одан да көп туынды класс арқылы анықталмайтын емес екенін ескеріңіз.

#include 

struct A
{
    A() { foo (); }
    virtual void foo() { std::cout << "A::foo" << std::endl; }
};

struct B : public A
{
    virtual void foo() { std::cout << "B::foo" << std::endl; }
};


int
main ()
{
    B b;     //prints "A::foo"
    b.foo ();//prints "B::foo"
    return 0;
}
0
қосылды
@ Пабби8: Бұл жағдайда конструктор статикалық диспетчерді пайдалана алады, бірақ ол барлық жағдайларда жасалмайды. Атап айтқанда, конструктор диспетчер функциясын A сілтемесінен өтіп, диспетчер виртуалды функцияны шақыратын болса, сол нәтиже стандарт бойынша мандатқа ие болады және бұл жағдайда компилятор статикалық диспетчерді пайдалана алмайды. қоңырау шыққан диспетчерді өңдеу кезінде ол білмейді.
қосылды автор David Rodríguez - dribeas, көзі
ISO/IEC 14882: 2011 (E) [class.cdtor] 12.7 Құрылыста және жоюда [# 4] «Құрылыста немесе виртуалды функцияларда (10.3) мүше функциялары құрылыста немесе жою (12.6.2). «
қосылды автор chill, көзі

Бұл іске асырылу тәуелді болса да, конструктордың денесі сіз C ++ spec (12.7/3) бойынша рұқсат етілгендіктен, ол this арқылы статистикалық емес әдістерге қол жеткізу үшін, код> көрсеткіш конструктор корпусында ... сондықтан vtable конструктор денесі алдында орнату керек еді, әйтпесе this көрсеткіші арқылы виртуалды класс әдістерін шақыру дұрыс жұмыс істемейді. this көрсеткіші және vtable екі түрлі нәрсе болса да, C ++ стандартының конструктор корпусында қолданылатын this көрсеткішіне мүмкіндік беретін фактісі компилятордың this көрсеткішінің дұрыс жұмыс істеуі үшін, ең болмағанда, уақыттық тұрғыдан стандартты түрде пайдалану үшін vtable бағдарламасын іске асыруы керек. Егер vtable конструкторлар денесінде немесе одан кейін баптандырылса, онда this көрсеткішін конструктордың корпусындағы виртуалды функцияларды шақыру немесе this көрсеткішін функцияларға бұл динамикалық жөнелтуге тәуелді болып, проблемалық болып табылады және анықталмаған мінез-құлықты қалыптастырады.

0
қосылды
@curiousguy: 3.7.4.3 параграфқа сәйкес сіз өзіңізге жауап бере аласыз (бұл сіз бұрын мәлімдегеннен, таныс болуыңыз керек).
қосылды автор Damon, көзі
@curiousguy: Иә, ИСО комитеті үшін жұмыс жасадыңыз ба? Мені таңдандырды. Осы талқылауды осы сайтта сұралған екі сұраққа байланыстыру арқылы аяқтаймын: stackoverflow.com/questions/32045888/… stackoverflow.com/questions/32100245/hellip; stackoverflow.com/questions/32048698/…
қосылды автор Damon, көзі
@curiousguy: Неліктен ең мықты адамдар ең агрессивті болып табылады ... Мен мұны талқылауға уақыт жұмсамауым керек. Конструктордан виртуалды функцияны шақыру дәл белгілі бір түрдегі белгілі функцияны шақыруды білдіреді. Немесе тек бір ғана үміткер бар (тек бір мұра), немесе сіз оны толығымен білуіңіз қажет болғандықтан (Мүлей мұрасында бірнеше белгісіз атаулар болса). Бұл динамикалық жіберу емес, бірақ статикалық. Бірақ, әрине, ол туралы не білетінін білетін адам ретінде сіз білесіз. :-)
қосылды автор Damon, көзі
@curiousguy: ЦТордан шақырылған кез-келген виртуалды функция толығымен білікті атпен (бірнеше мұрагерлік және бірнеше базалық сыныптағы бірдей атаулармен жұмыс істеген жағдайда) немесе ол қандай базалық класс тиесілі екенін біледі (аты-жөні бірегей немесе бір ғана базалық класс бар) немесе құрастыру үзілістері (қоңырау біркелкі емес). Нысанның статикалық ( емес динамикасы!) Түрін белгілі бір анықтамамен анықтағандықтан, бұл көрсеткіштің техникалық қажеті де жоқ, және қай функцияны атау керек екендігі белгілі. Бұл ерекше оқиға емес, тек бір ғана жағдай.
қосылды автор Damon, көзі
Ия, бірақ бұл this үшін ғана жарамды. Жоғарыдағы пікірімді қараңыз. Виртуалды функциялардың тиесілі класы конструкторда белгілі, оларды шешу үшін қажет болмайды.
қосылды автор Damon, көзі
@Damon Сіз не туралы сөйлегеніңіз туралы ештеңе білмейміз. Білікті қоңыраулар мұнда маңызды емес. Виртуалды функцияға сәйкессіз қоңыраулар динамикалық түрді, кезеңді қолданады.
қосылды автор curiousguy, көзі
@Damon Көрсеткіштер тривиалдық емес па деп санайсыз ба?
қосылды автор curiousguy, көзі
@Damon Егер сіз «Қауіпсіздендірілген көрсеткіштер» менің кейбір сұрақтарға жауап болса, жауап беріңіз.
қосылды автор curiousguy, көзі
@Damon Осы сұрақтардың кез-келгеніне маған көмектесе аласыз ба?
қосылды автор curiousguy, көзі
@Damon ISO C ++ комитетінде жұмыс істедім. Сіз барсыз ба?
қосылды автор curiousguy, көзі
Сіз оңтайландырылған және жалпы жағдайда арнайы жағдайларды араластырасыз. Ерекше жағдайларда компилятор конструктордың басында vptr орнатуды болдырмайды, себебі кейінірек оны кейінгі сыныптың конструкторы орнатады. F.ex. конструктор this қолданбаса, функциялардың аргументі ретінде анық немесе толық емес; немесе функциялары енгізілгенде және т.б. Жалпы айтқанда, конструктор this қолданатын кез келген тәсілмен қолдануы мүмкін. Бұл әдеттегі көрсеткіш. виртуалды қоңырауларға арналған, олар «жалпыға бірдейлендірілмеуі» мүмкін емес (проблеманы тоқтату қараңыз).
қосылды автор curiousguy, көзі
@Damon Жоғарыдағы түсініктемеңіз дұрыс емес. Сіз ерекше жағдайды (бұзылған жағдайды) қарастырасыз, ол туралы дұрыс мәлімдеме жасайсыз, содан соң оны қорытындылаңыз. Бұл фактілер туралы дұрыс айтасыз: (1) конструктор корпусының ішінде, * this динамикалық түрі анықтамамен белгілі (2) компилятор қажеттілігі арқылы динамикалық түрі белгілі объектке виртуалды шақыру vptr пайдаланбаңыз. Және бұл бәрі. Сіз конструкторлар корпусында this туралы арнайы ереже бар деп ойлайсыз, бірақ жоқ.
қосылды автор curiousguy, көзі
vtable және this екеуі мүлдем басқаша болып табылады.
қосылды автор Pubby, көзі
@ Pubby8 ... толығымен келіседі, бірақ 1) конструкторлық органда статистикалық емес класс әдістеріне қол жеткізуге рұқсат етіледі және 2) егер сіз мұны this көрсеткіші арқылы жасасаңыз және vtable параметрі орнатылмаса, онда сіз статистикалық емес виртуалды функцияларды шақыратын кейбір мәселелерді іске қосасыз, себебі виртуалды функционалдық қоңыраулар сынып кодының this көрсеткішіне нұсқайды.
қосылды автор Jason, көзі