如何从base的构造函数中的基类方法调用派生类方法

时间:2010-08-18 18:59:29

标签: c++ class virtual base

我想知道是否可以在基本构造函数调用的函数内调用派生类函数(当括号中的代码执行时,是否已经创建了它?)

#pragma once
class ClassA
{
public:
 ClassA(void);
 virtual ~ClassA(void);

 void Init();

protected:
 short m_a;
 short m_b;

 virtual void SetNumbers(short s);
};

include "ClassA.h"
#include <iostream>


ClassA::ClassA(void) : m_a(0), m_b(0)
{
Init();
}


ClassA::~ClassA(void)
{
}

void ClassA::SetNumbers(short s)
{
 std::cout << "In ClassA::SetNumbers()\n";
 m_a = s;
 m_b = s;
}

void ClassA::Init()
{
 this->SetNumbers(2);
}

#pragma once
#include "ClassA.h"
class ClassB : public ClassA
{
public:
 ClassB(void);
 virtual ~ClassB(void);

 virtual void SetNumbers(short);

 int x;
};

#include "ClassB.h"
#include <iostream>


ClassB::ClassB(void)
{
}


ClassB::~ClassB(void)
{
}

void ClassB::SetNumbers(short s)
{
 std::cout << "In ClassB::SetNumbers()\n";

 m_a = ++s;
 m_b = s;

 ClassA::SetNumbers(s);
}

有任何建议吗?...

提前谢谢你:) ...

5 个答案:

答案 0 :(得分:2)

你不能这样做的原因很简单,即在构造基类时,派生类甚至还没有开始构造。您不能在尚不存在的对象上调用成员函数。

实际上,即使你设法调用SetNumbers并在初始化之前分配派生类的成员变量,它们肯定会在最终初始化时被覆盖。我承认对此进行推理是没有意义的,因为我们在定义的行为之外会很好。

答案 1 :(得分:2)

没有。 B的所有部分(以A开头,因为它的基础)是在调用B的构造函数之前构造的。因此,在调用SetNumbers时,B的任何部分(A部分除外)都没有构建 - 并且可能包含v表,因此无法知道该调用的去向去吧。

当然,有一个简单的解决方案:从B的构造函数中调用B :: SetNumber()(毕竟,B的构造函数的目的)

答案 2 :(得分:1)

不,抱歉。 :(它可能在一个或两个C ++编译器中编译,但不推荐。来自C++ FAQ Lite section 10.7

  

[10.7]你应该使用这个指针吗?   在构造函数中?

     

[...剪断...]

     

这是永远不会有效的事情:   构造函数的{body}(或者   从构造函数调用的函数)   无法归结为派生类   调用虚拟成员函数   在派生类中重写。如果   你的目标是达到被覆盖的目标   你在派生类中的函数   不会得到你想要的。请注意你   将无法进入覆盖   派生类独立于你   调用虚拟成员函数:   显式使用this指针   (例如,this-&gt; method()),隐含地   使用这个指针(例如,   method()),甚至调用其他一些   调用虚拟成员的函数   对你的这个对象起作用。该   底线是这样的:即使   调用者正在构建一个对象   派生类,在构造函数中   基类的,你的对象不是   那派生的课程。你有   被警告过。

注意:强调我的。

链接的更多详情

答案 3 :(得分:1)

唯一可以做到这一点的是从某个模板派生出来的东西:

template<typename T> class base
{
  T* down_cast() throw()
  {
    return static_cast<Derived*>(this);
  }
  const T* down_cast() const throw()
  {
    return static_cast<const Derived*>(this);
  }
public:
  base()
  {
    down_cast()->doSomething();
  }
 /* … */
};

class derived : private base<derived>
{
public:
  void doSomething()
  {
  }
};

请注意,doSomething是公开的而非虚拟的。

我们可以将static_cast导出,因为已知派生是派生类型。

从基础参数化中获取某些东西是最好的时候做的奇怪的事情。据说,当微软的ATL团队使用它时,他们问C ++编译器团队是否有效并且没有人确定,尽管它是有效的,因为模板构造依赖于名称如下:

首先,模板可用,但不在类中使用。然后,名称derived可用。然后它实例化base<derived>的布局 - 这需要知道成员变量和虚函数,只要这些都不依赖于derived布局的知识(指针和引用很好),这将是一切顺利。然后它将创建derived的布局,最后它将创建derived的成员函数,其中可能包括为base<derived>创建成员函数。因此,只要base<derived>不包含派生成员变量(基类永远不能包含从自身派生的类型的成员变量)或需要知道派生的布局的虚函数,我们确实可以做到冒险 - 看上面的遗产。

这包括能够在施工期间从derived调用base的非虚拟公共成员,因为它已经是基础的一部分。这有很大的局限性。特别是,如果doSomething()依赖于derived构造函数中构造的任何内容,那么它将无效,因为derived尚未构建。

现在,这真的是个好主意吗?否。

答案 4 :(得分:0)

一个简单的设计解决方案是使用聚合而不是继承