2014-03-05 165 views
3

我想知道是否有无论如何,我的超类可以调用子类的函数initValues()而不必重写构造函数?从超类访问子类?

下面的代码:

#ifndef VECTOR_MATH_H 
#define VECTOR_MATH_H 

#include "GL\glew.h" 

#include <iostream> 

namespace Math3d 
{ 

class Vector 
{ 
public: 
    Vector(int length=2) : v(new float[length]) { initValues(); } 
    ~Vector() { delete[] v; } 
protected: 
    virtual void initValues() 
    { 
     std::cout << "Vector" << std::endl; 
    } 
    float* v; 
}; 

class Vector3 : public Vector 
{ 
public: 
protected: 
    void initValues() 
    { 
     std::cout << "Vector3" << std::endl; 
    } 
}; 

} 

#endif 

然后我创建这样一个变量: 的Vector3 VEC;

然后我想要调用子类Vector3的initValues() 方法 。

这可能吗?

+0

似乎是重复的:http:// stackoverflow。com/questions/7644154/calling-base-class-virtual-method-by-derived-class-virtual-method –

+0

@StevenBehnke看起来像OP的另一种要求。 –

+0

它可能是,但反过来对我没有任何意义。 Vector如何知道派生类? –

回答

2

简要的回答:不,你不能。

长答案:直到派生类构造函数被调用后,对象的虚拟表才被充实。在基类构造函数中,虚拟表指向函数的基类实现。如果基类有一个实现,那么该函数将被调用。如果基类没有实现,则会发生依赖于平台的错误/异常。

+0

至少有可能,看看我的答案。 –

+0

正如您已经显示的那样,在类模板中使用常规函数是可能的,并且它是众所周知的[好奇重复的模板模式](http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)。这是不可能的使用虚拟功能。 –

+0

实际上,对于CRTP而言,函数是否声明为虚拟或完全没有关系。 –

2

如果要同时调用父类initValues(),然后将子类initValues()你将需要显式调用Vector::initValues()Vector3::initValues()因为动态调度总是会调用该方法的更为特殊的版本:

void Vector3::initValues() { 
    Vector::initValues(); 
    other specific code; 
} 

如果您真的希望大家都能够在你想,那么你将需要第二个方法的顺序:

class Vector { 
    protected: 
    void initValues() { 
     // common init 
     specificInitValues(); 
    } 

    virtual void specificInitValues() = 0; 
}; 

class Vector3 : public Vector { 
    protected: 
    virtual void specificInitValues() override { 
    // specific init 
    } 
}; 
+1

我回答了同样的问题。但是OP从基类构造函数调用'initValues()',所以我觉得他们可能真的在问一些不同的东西。 – juanchopanza

+0

您无法从基类安全地调用派生类的方法,因为该对象可能只是基类的对象,或属于非常不同的派生类。重新考虑你的设计,这应该永远不需要。 – vonbrand

0

你不能用动态多态性做到这一点(使用virtual功能表,又名。如vtable),因为在超类试图调用虚方法的时候,只有双类被构造出来,并且子类initValues()的实现不能从完全构造的vtable中调用。

有两种方法来解决这个问题:

1.让您initValues()方法公开,并要求其施工后从客户称为

2.你可以做些什么来实现这种行为,就是用静态的,而不是多态:

template<class Derived> 
class VectorSuper 
{ 
public: 
    VectorSuper(int length=2) 
    : v(new float[length]) 
    { 
     static_cast<Derived*>(this)->initValues(); 
    } 
    ~VectorSuper() { delete[] v; } 
protected: 
    void initValues() // Note, no virtual 
    { 
     std::cout << "VectorSuper" << std::endl; 
    } 
    float* v; 
}; 

class VectorSub 
: public VectorSuper<VectorSub> 
{ 
protected: 
    void initValues() // Note, no virtual 
    { 
     VectorSuper<VectorSub>::initValues(); 
     std::cout << "VectorSub" << std::endl; 
    } 
} 

后一种方式可能会查询超类中实现的抽象接口的某些进一步区别,以便在不知道约VectorSub且不需要的上下文中合理使用。

class AbstractVector 
{ 
public: 
    virtual ~AbstractVector() = 0; 
    // example interface 
    virtual float operator[](int index) const = 0; 
}; 

template<class Derived> 
class VectorSuper 
: public AbstractVector 
{ 
public: 
    VectorSuper(int length_=2) 
    : length(length_), v(new float[length]) 
    { 
     static_cast<Derived*>(this)->initValues(); 
    } 
    ~VectorSuper() { delete[] v; } 

    virtual float operator[](int index) const 
    { 
     if(index >= length || index < 0) 
     { 
      throw std::invalid_argument("index"); 
     } 
     return v[index]; 
    } 
protected: 
    // ... as before 

    int length; // Remember length additionally! 
    float* v; 
};