2011-10-26 20 views
3

所以我有这种设置实施前调用虚函数

class Base 
{ 
public: 
    Base(); 
    virtual void parse() = 0; 
}; 

Base::Base() 
{ 
    parse(); 
} 

class Sub : public Base 
{ 
public: 
    Sub(); 
    void parse(); 
}; 

Sub::Sub() : Base() {} 

void Sub::parse() 
{ 
    // DO stuff 
} 

我不知道是否有安韦我可以做一些类似的,现在我得到那个说我可以到一个错误不要称为纯粹的虚拟功能,这是有道理的。有没有关键字可以用来完成这项工作?

我认为使parse()只是虚拟的,而不是纯粹的虚拟会起作用,但我希望用户必须覆盖它。

回答

10

在构造函数(或析构函数)根本不会让你在一个重写的版本,功能在派生类中最终调用虚成员函数。

原因是基类构造函数(析构函数)在派生类的构造函数(析构函数)之前(之后)被执行。这意味着表示派生类的对象的部分仅仅是尚未(不再是)现有。并且在不存在的对象上调用成员函数是不可能的。

您将需要实现的一种形式两阶段建设为了做自己想做的(未内置到语言)。通常这是通过具有包装类首先完全构造一个Sub对象,然后才调用parse()就可以了。

+1

+1,简而言之,任何类的构造函数中的* this *指向相同的类,而不管它从何处被调用。 –

+0

你为什么不直接在你的回答中使用我的链接? http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.6 – mkb

+0

@Als:尽管他的确是一个正确的陈述,但对于新手来说,这也是相当难以理解的。 ':)' – sbi

2

这里的要点是,你不能在构造函数中调用纯虚函数,即使你要提供一个实现,只有基类的实现将在构造函数中使用。

原因很简单,Base的构造函数是在Sub的开头执行的,所以对Sub的任何虚函数调用都会在不完整的对象上调用。

一般来说,没有解决方案:您无法在构造函数或析构函数中调度虚函数。

2

虚拟函数的解析无法找到尚未构建的类 中的函数。 (形式上: 对象的动态类型是正在运行的构造函数或析构函数的对象。)所以 您需要某种后构造函数,该语言不支持 。

您可以通过使用一个虚拟的参数,它调用 你在析构函数所需要的功能,例如排序工作围绕它的:

class Base 
{ 
public: 
    class PostConstructor 
    { 
     Base* owner; 
     friend class Base; 
    public: 
     PostConstructor() : owner(NULL) {} 
     ~PostConstructor() { owner->parse(); } 
    }; 
    Base(PostConstructor const& helper) 
    { 
     helper.owner = this; 
    } 
}; 

class Derived : public Base 
{ 
public: 
    Derived(PostConstructor const& helper = PostConstructor()) 
     : Base(helper) 
    { 
    } 
}; 

或者如果该类参数:

class Base 
{ 
    std::string name; 
public: 
    class PostConstructor 
    { 
     Base* owner; 
     std::string arg; // for example... 
     friend class Base; 
    public: 
     PostConstructor(std::string const& arg) // implicit conversion!! 
       : owner(NULL), arg(arg) {} 
     ~PostConstructor() { owner->parse(); } 
     operator std::string() const { return arg; } 
    }; 
    Base(PostConstructor const& helper) 
     : name(helper) 
    { 
     helper.owner = this; 
    } 
}; 

class Derived : public Base 
{ 
public: 
    Derived(PostConstructor const& helper) 
     : Base(helper) 
    { 
    } 
}; 

这是可行的,因为PostConstructor将会是一个临时的,被破坏的 在完整表达式的末尾(当Derived已经完全被 构造时)。

在这两种情况下:派生类必须合作(但如果他们不这样做,它们不会被编译为 )。如果在更复杂的表达式中构造类 ,该技巧也可能失败。东西 这样的:

Derived(...).someFunction(); 

在这种情况下,充分表达年底将返回 从someFunction之后,这可能是有点晚了打电话parse()

尽管有局限性,但我发现这种情况很有用。

+1

建设性的批评。你的答案和“解决方法”是正确的,但对于非专业程序员来说似乎很复杂。 – umlcat

+0

@umlcat解决方法肯定有一些缺点,并不是一个通用的解决方案。这是我期望任何C++在看到它时能够理解的东西(提供解释我在发布的文本部分中放置的内容的注释)。然而,仍然有人学习C++可能会遇到问题。 –

0

抽象或纯方法是虚方法的一个特例(所有抽象或纯方法都是虚拟的)。

我以前的答案是错误的,因为我忽略了构造函数。 C++上的构造函数不是虚拟的,并且不允许在构造函数内调用虚拟(抽象而非抽象方法)。如果你从另一个不是构造函数的方法调用一个非抽象的overrided“parse”,那就没问题。

问题不在于其方法的抽象,它是从构造函数中调用的。

#include <conio> 

class Base 
{ 
public: 
    // a constructor: 
    Base(); 

    // "virtual and abstract" method: 
    virtual void parse() = 0; 

    // "virtual non abstract" method: 
    virtual void hello(); 
}; 

// Error: you cannot call a virtual method from a constructor, 
// wheter is abstract or not: 
Base::Base() 
{ 
    // error: 
    parse(); 

    // error: 
    hello(); 
} 

Base::hello() 
{ 
    cout << "Hello World\n"; 
} 

class Sub : public Base 
{ 
public: 
    Sub(); 

    // forgot "virtual" here, 
    // other languages use "override" instead, here: 
    virtual void parse(); 
    // another "overriden" methods: 
    virtual void parse(); 
}; 

// right: its important to call the base constructor, 
// in this case: 
Sub::Sub() : Base() 
{ 
    // ... 
} 

void Sub::parse() 
{ 
    // DO stuff 
} 

int main() 
{ 
    Base *MyBaseObject = new Base(); 
    MyObject->parse();  

    Sub *MyObject = new Sub(); 
    MyObject->parse(); 

    return 0; 
} 

这是一个解决方法。要调用一个虚拟的方法, 就像是从构造方法中调用,宣布了新的方法,它的构造后立即叫:

#include <conio> 

class Base 
{ 
public: 
    // a constructor: 
    Base(); 

    // a "postconstructor" or "pseudoconstructor" 
    virtual void create(); 

    // "virtual and abstract" method: 
    virtual void parse() = 0; 

    // "virtual non abstract" method: 
    virtual void hello(); 
}; 

// Error: you cannot call a virtual method from a constructor, 
// wheter is abstract or not: 
Base::Base() 
{ 
    // no virtual methods called here, 
    // wheter abstract or not 
} 

// its not a real constructor, just a virtual method: 
void Sub::create() 
{ 
    // ... 
} 
Base::hello() 
{ 
    cout << "Hello World\n"; 
} 

class Sub : public Base 
{ 
public: 
    Sub(); 

    virtual void create(); 

    // forgot "virtual" here, 
    // other languages use "override" instead, here: 
    virtual void parse(); 
    // another "overriden" methods: 
    virtual void parse(); 
}; 

// right: its important to call the base constructor, 
// in this case: 
Sub::Sub() : Base() 
{ 
    // ... 
} 

// its not a real constructor, just a virtual method: 
void Sub::create() : create() 
{ 
    parse(); 
} 

void Sub::parse() 
{ 
    // DO stuff 
} 

int main() 
{ 
    // this commented code, wont work 
    /* 
    Base *MyBaseObject = new Base(); 
    MyObject->create();  
    MyObject->parse();  
    */ 

    // calling "pseudo-constructor", 
    // just after real constructor 
    Sub *MyObject = new Sub(); MyObject->create(); 
    MyObject->parse(); 

    return 0; 
} 

我的错误道歉。