我一直在努力解决这个问题。给定三个相互继承的类,序列如何工作?例如:Classes Vehicle->Car->Mercedes
很容易创建 MCVE ...
#include <chrono>
// 'compressed' chrono access --------------vvvvvvv
typedef std::chrono::high_resolution_clock HRClk_t; // std-chrono-hi-res-clk
typedef HRClk_t::time_point Time_t; // std-chrono-hi-res-clk-time-point
typedef std::chrono::milliseconds MS_t; // std-chrono-milliseconds
typedef std::chrono::microseconds US_t; // std-chrono-microseconds
typedef std::chrono::nanoseconds NS_t; // std-chrono-nanoseconds
using namespace std::chrono_literals; // support suffixes like 100ms, 2s, 30us
#include <iostream>
#include <iomanip>
class Vehicle_t
{
public:
Vehicle_t () { std::cout << "\n Vehicle_t" << std::flush; }
~Vehicle_t () = default;
};
class Car_t
: public Vehicle_t
{
public:
Car_t () { std::cout << "\n Car_t" << std::flush; }
~Car_t () = default;
};
class Mercedes_t
: public Car_t
{
public:
Mercedes_t () { std::cout << "\n Mercedes_t" << std::flush; }
~Mercedes_t () = default;
};
int main(int , char** )
{
int retVal = -1;
{
Time_t start_us = HRClk_t::now();
Mercedes_t m;
retVal = 0;
auto duration_us = std::chrono::duration_cast<US_t>(HRClk_t::now() - start_us);
std::cout << "\n\n duration " << duration_us.count() << " us" << std::endl;
}
return(retVal);
}
带输出
Vehicle_t
Car_t
Mercedes_t
初学者注意事项:
我相信你以前听说过,“执行开始于 'main'”。这段代码也可以。
因此声明,“Mercedes_t m;” 是第一个(对于这个类层次结构)。它显示了首先调用“Mercedes_t”构造函数(最派生的)的调用。
请记住,所有方法(和函数)都有入口和出口。
派生程度最高的 ctor 调用“Car_t” ctor,后者调用“Vehicle_t” ctor。
输出显示“Vehicle_t”ctor cout 语句首先完成。因此你可以推断这个ctor首先完成。
输出显示下一个要完成的 ctor 是“Car_t”,然后是“Mercedes_t”
概括
最后调用最基类,但最先完成。
派生程度最高的类首先被调用,但最后完成。
ctor Entry/Invocation sequence: Mercedes_t -> Car_t -> Vehicle_t ctor Exit/ctor completion seq : Vehicle_t -- Car_t -- Mercedes_t
更新 - 11/19/2017
我认为我的大部分答案都是关于实现细节的。我突然想到,我上面提供的这种“证据”是不够的。(并与一个或多个备选答案相矛盾)。
更有说服力的“证据”是检查装配。以下代码与上面的 MCVE 有所不同,但仍然具有可识别的名称。仅供参考 - 我的系统是 Ubuntu 15.10,g++ 报告 (Ubuntu 5.2.1-23ubuntu1~15.10) 5.2.1。
使用gdb,我在main(即b main)处设置断点,发出run,然后使用命令
(gdb) 反汇编 /m
并发现
154 { 155 Mercedes_t m; // invoke Mercedes_t ctor 0x00000000004019e2 <+40>: lea -0x30(%rbp),%rax 0x00000000004019e6 <+44>: mov %rax,%rdi 0x00000000004019e9 <+47>: callq 0x40214a <Mercedes_t::Mercedes_t()> 0x00000000004019ee <+52>: lea -0x30(%rbp),%rax
这证实了创建对象的第一个调用是 < Mercedes_t::Mercedes_t() > ctor,即最先调用最派生的 ctor。
接下来,从Mercedes_t ctor的同类型反汇编中,可以看到对<Car_t::Car_t()>的调用。
Dump of assembler code for function Mercedes_t::Mercedes_t():
91 Mercedes_t () : initLineNum (log(__LINE__)) // Mercedes_t ctor init
0x000000000040214a <+0>: push %rbp
0x000000000040214b <+1>: mov %rsp,%rbp
0x000000000040214e <+4>: sub $0x10,%rsp
0x0000000000402152 <+8>: mov %rdi,-0x8(%rbp)
=> 0x0000000000402156 <+12>: mov -0x8(%rbp),%rax
0x000000000040215a <+16>: mov %rax,%rdi
0x000000000040215d <+19>: callq 0x4020ec <Car_t::Car_t()>
0x0000000000402162 <+24>: mov $0x5b,%edi
0x0000000000402167 <+29>: callq 0x401616 <log(int)>
0x000000000040216c <+34>: mov %eax,%edx
0x000000000040216e <+36>: mov -0x8(%rbp),%rax
0x0000000000402172 <+40>: mov %edx,0x8(%rax)
对我来说够多的了。我强烈建议你学习 gdb。
但同样,我认为这些可能是实现细节。
更新 - 2017 年 11 月 20 日添加了识别复制构造函数序列的代码。
我将复制 ctor 添加到每个类(在默认 ctor 之后,在 dtor 之前):
Vehicle_t(const Vehicle_t& /*rhs*/){
std::cout << "\n Vehicle_t(rhs) " << std::flush; }//Vehicle_t copy ctor (3)
Car_t(const Car_t& rhs) : Vehicle_t(rhs) {
std::cout << "\n Car_t(rhs) " << std::flush; } // Car_t copy ctor (3)
Mercedes_t(const Mercedes_t& rhs) : Car_t(rhs) {
std::cout << "\n Mercedes_t(rhs) " << std::flush; } // Mercedes_t copy ctor (3)
在 main 中添加了 2 行,在默认 ctor 之后,在“retVal = 0;”之前
Mercedes_t m; // invoke default ctor
std::cout << "\n";
Mercedes_t m2(m); // invoke copy ctor
retVal = 0;
输出现在看起来像:
Vehicle_t
Car_t
Mercedes_t
Vehicle_t(rhs)
Car_t(rhs)
Mercedes_t(rhs)
这与默认 ctors 的顺序相同。
我还使用了 gdb 和“disassemble /m”命令并检查了组件。调用序列与默认构造函数匹配。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句