2015-06-11 14 views
10

Ho provato a "riparare" l'esempio in this answer per dimostrare come è possibile chiamare una funzione virtuale pura.Perché non si tratta di una chiamata di pura funzione virtuale?

#include <iostream> 
using namespace std; 

class A 
{ 
    int id; 
public: 
    A(int i): id(i) {} 
    int callFoo() { return foo(); } 
    virtual int foo() = 0; 
}; 

class B: public A 
{ 
public: 
    B(): A(callFoo()) {} 
    int foo() { return 3; } 
}; 

int main() { 
    B b; // <-- this should call a pure virtual function 
    cout << b.callFoo() << endl; 
    return 0; 
} 

Ma ottengo alcun errore di runtime here (with C++ 4.9.2), ma l'uscita 3. Ho provato la stessa cosa con Borland C++ 5.6.4, ma ci sto diventando una violazione di accesso. Penso che foo() debba essere virtuale puro nella chiamata del costruttore della classe base.

Chi ha torto? Dovrei provare più compilatori? Ho ragione nella mia comprensione delle funzioni virtuali?

+4

Non utilizzerei i risultati di un test con Borland C++ per verificare se uno snippet di codice è valido e/o conforme allo standard;) – CoryKramer

+0

@CoryKramer Le funzioni virtuali sono in uso da decenni. – Wolf

+1

Ne sono consapevole, stavo facendo un'osservazione sfacciata su qualcuno che utilizza ancora gli IDE Borland – CoryKramer

risposta

14

Il codice ha un comportamento non definito: è UB che chiama una funzione membro su un oggetto (anche non virtuale) prima che tutte le sue classi base siano state inizializzate. C++ 14 (n4140) 12.6.2/14, enfasi miniera:

Le funzioni membro (incluse le funzioni membro virtuale, 10.3) possono essere chiamate per un oggetto in costruzione. Analogamente, un oggetto in costruzione può essere l'operando dell'operatore typeid (5.2.8) o di un dynamic_cast (5.2.7). Tuttavia, se queste operazioni vengono eseguite in un ctor-inizializzatore (o in una funzione chiamata direttamente o indirettamente da un ctor-inizializzatore) prima di tutte le mem-inizializzatori per classi di base sono completate, il risultato l'operazione non è definita. ...

ctor-inizializzatore è l'intero elenco seguente :. mem-initializer è un elemento di questo elenco.

+0

Vero e questa regola UB è ben fondata. Ma questo non fa altro che muovere il mal di testa: nessun compilatore o controllore di codice statico troverà tutti gli errori. Sono rimasto scioccato dal fatto che tali semplici casi in linea non siano stati immediatamente respinti. – Wolf

+0

@Wolf: il compilatore [non è necessario per eseguire un'analisi completa del flusso di controllo] (http://blogs.msdn.com/b/oldnewthing/archive/2013/10/11/10455907.aspx), poiché ciò potrebbe non è possibile eseguire staticamente in fase di compilazione. – Kevin

+0

@Kevin Implementare funzioni virtuali pure per sbarazzarsi di * pura funzione virtuale chiamata * messaggi mi sembra un uso improprio o è stato progettato proprio per questo scopo? – Wolf

5

La dichiarazione B b; chiama il costruttore predefinito su B.

Durante la costruzione di B, nulla di pertinente a B è stato costruito fino a quando A non è completamente costruito.

Quindi, nel tentativo di chiamare callFoo(), il comportamento non è definito poiché non è possibile fare affidamento sulla tabella v per la classe B impostata.

In breve: il comportamento di chiamare una funzione virtuale pura durante la costruzione di una classe astratta non è definito.

+0

Ho immaginato che sarebbe stato così perché. Quindi, sembra che sia stato un po 'pigro con il mio suggerimento di "riparazione". – Wolf