Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/mercurial/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C+的问题+;抽象类(我可以用Java实现,但不能用C+;+;!)_C++_Subclass_Abstract_Pure Virtual - Fatal编程技术网

C++ C+的问题+;抽象类(我可以用Java实现,但不能用C+;+;!)

C++ C+的问题+;抽象类(我可以用Java实现,但不能用C+;+;!),c++,subclass,abstract,pure-virtual,C++,Subclass,Abstract,Pure Virtual,首先,我搜索了这个问题,发现了很多类似的问题,但我找不到解决问题的答案。如果这只是我的愚蠢,我很抱歉 我想做的是让抽象类的构造函数调用一个纯虚拟的函数。在Java中,这是可行的,因为子类提供了被调用的抽象方法的实现。但是,在C++中,我得到了这个链接错误: test.o:test.cpp:(.text$_ZN15MyAbstractClassC2Ev[MyAbstractClass::MyAbstractClass ()]+0x16): undefined reference to `MyAbs

首先,我搜索了这个问题,发现了很多类似的问题,但我找不到解决问题的答案。如果这只是我的愚蠢,我很抱歉

我想做的是让抽象类的构造函数调用一个纯虚拟的函数。在Java中,这是可行的,因为子类提供了被调用的抽象方法的实现。但是,在C++中,我得到了这个链接错误:

test.o:test.cpp:(.text$_ZN15MyAbstractClassC2Ev[MyAbstractClass::MyAbstractClass
()]+0x16): undefined reference to `MyAbstractClass::initialize()'
collect2: ld returned 1 exit status
这是我的密码:

#include <iostream>

class MyAbstractClass {
protected:
    virtual void initialize() = 0;

public:
    MyAbstractClass() {
        initialize();
    }
};

class MyClass : public MyAbstractClass {
private:
    void initialize() {
        std::cout << "yey!" << std::endl;
    }
};

int main() {
    MyClass *my = new MyClass();
    return 0;
}

这个代码打印“Yey!”。非常感谢任何帮助

正如@Seth所解释的,您不能在构造函数中调用虚拟函数。更具体地说,虚拟调度机制在构建和销毁期间被禁用。要么使您的
初始化
成员函数为非虚拟函数,并在基类中实现它,要么让用户显式调用它。

让我在这里引用Scott Meyers(请参阅):

第9项:在构造或销毁期间,切勿调用虚拟函数

我将从概述开始:在构造或销毁期间不应该调用虚拟函数,因为这些调用不会按照您的想法执行,如果它们执行了,您仍然会不高兴。如果你是一个正在恢复的java或C程序员,请密切注意这个项目,因为这是一个地方,那些语言Zigg,而C++ +ZAG.< 问题:在对象构造期间,虚拟函数表可能尚未准备就绪。想象一下,你的类在继承方面排名第四。构造函数是按继承顺序调用的,因此在调用这个纯虚拟(或者即使它是非纯的)时,您希望基类为尚未完成的对象调用
initialize

MyAbstractClass() {
    initialize();
}
它不会执行到
MyClass::initialize()
的虚拟分派,因为在对象构造的这个阶段,尚未创建其
MyClass
部分。因此,您实际上是在调用
MyAbstractClass::initialize()
,因此必须对其进行定义。(是的,可以定义纯虚拟成员函数。)

尽量避免从构造函数调用虚拟成员函数,因为这类事情会发生,并且会让您措手不及。这样做很少有意义

另外,尽量避免使用
initialize()
函数;您已经有了可以使用的构造函数


更新
实际上,尽管您可以将上述内容视为对任何其他虚拟成员函数的读取,但从构造函数调用纯虚拟成员函数会产生未定义的行为。所以不要尝试 在这种情况下,你不需要;派生类的构造函数将在基类的构造函数之后调用,因此您可以通过以下方式获得所需的结果:

#include <iostream>

class MyAbstractClass {
public:
    MyAbstractClass() {
        // don't do anything special to initialise the derived class
    }
};

class MyClass : public MyAbstractClass {
public:
    MyClass() {
        std::cout << "yey!" << std::endl;
    }
};

int main() {
    MyClass my;
    return 0;
}
#包括
类MyAbstractClass{
公众:
MyAbstractClass(){
//不要做任何特殊的事情来初始化派生类
}
};
类MyClass:公共MyAbstractClass{
公众:
MyClass(){

std::cout在构造和销毁期间,为正在构造或销毁的基本子对象适当地设置虚拟表。这在理论上是正确的,因为派生类不是活动的(其生存期尚未开始或已经结束)C++的边已经被其他的答案处理了,但是我想在它的java方面加一个注释。从构造函数<强>调用一个虚函数是< /强>,在所有的情况下,不仅仅是C++中的问题。基本上代码是想在一个尚未创建的对象上执行一个方法,这是一个错误。

不同语言中实现的两种解决方案在试图对代码所做的尝试有所不同。在C++中,决策是在构建一个基本对象时,直到派生对象的构建开始,对象的实际类型是基,这意味着将不会有动态调度。在任何时候,对象的类型都是正在执行的构造函数的类型[*]。虽然这让一些人(包括您)感到惊讶,但它为问题提供了一个合理的解决方案

[*]相反,析构函数。随着大多数派生构造函数的完成,类型也会发生变化

在Java中,对象是从最终开始的最终类型,甚至在构建完成之前。在爪哇中,正如您所演示的,调用将被调度到最终重写器(我在这里使用C++ java俚语:到执行链中的虚拟函数的最后实现)。,这可能会导致不必要的行为。例如,考虑<代码>初始化()/<代码>:

的实现。 上一个程序的输出是什么?(在底部回答)

不仅仅是一个玩具示例,请考虑<代码> MyClass <代码>提供了一些在构建时设置并保存对象的整个生命周期的不变量。也许它保存了一个对数据可以被转储的记录器的引用。通过查看类,可以看到在构造函数中设置了记录器,并假定它不能。在代码中的任意位置重置:

public class MyClass extends MyAbstractClass {
   Logger logger;
   MyClass() {
      logger = new Logger( System.out );
   }
   void initialize() {
      logger.debug( "Starting initialization" );
   }
}
您现在可能看到了这一点。通过查看
MyClass
的实现,似乎没有任何错误。
logger
在构造函数中设置,因此可以在类的所有方法中使用。现在的问题是,如果
MyAbstractClass
调用被调度的虚拟函数然后应用程序将因NullPointerException而崩溃

现在我希望你理解和重视C++的不执行动态调度的决定,从而避免执行。
public class MyClass extends MyAbstractClass {
   final int k1 = 1;
   final int k2;
   MyClass() {
      k2 = 2;
   }
   void initialize() {
      System.out.println( "Constant 1 is " + k1 + " and constant 2 is " + k2 );
   }
}
public class MyClass extends MyAbstractClass {
   Logger logger;
   MyClass() {
      logger = new Logger( System.out );
   }
   void initialize() {
      logger.debug( "Starting initialization" );
   }
}