我对C++
不熟悉,但我来自Java
背景,因此我了解了大多数OOP
概念。我正在阅读介绍性指南,我遇到了这个例子:
[Foo.H]
class A
{
public:
A(int something);
};
class B : public A
{
public:
B(int something);
};
[Foo.C]
#include "Foo.H"
A::A(int something)
{
printf("Something = %d\n", something);
}
B::B(int something) : A(something)
{
}
假设通过将A(something)
传递给B::B(int something)
的初始化列表,它与java中的super
关键字类似 - 也就是说,它将执行{{1}我的代码?另外,为什么我只从initalizer列表中调用A::A(int something)
而不是A(something)
?
基本上我在问:以上是否相当于:
A::A(something)
让我扩展为什么我感到困惑。
如果我要使用:
B::B(int something)
{
A::A(something)
}
通过
调用代码B::B(int something) : A(something)
{
int x = 5;
printf("x = %d", x);
}
这会先打印B::B(7);
还是x = 5
?为什么它会按此顺序执行?
我只是对语法感到有点困惑,这使得即使在这个简单的例子中也很难掌握和可视化继承。
答案 0 :(得分:2)
是的,A(something)
传递的值与super
非常相似。您不必使用A::A
,因为该语言会自动将基类名称注入子类。
但它并不等同于
B::B(int something)
{
A::A(something)
}
因为那不是合法代码。您只能在初始化列表中选择要调用的父构造函数。
至于你关于印刷的问题......你试过吗?您会看到父something=
首先打印后跟x=
。
答案 1 :(得分:1)
不,它们不等同
子类的构造函数将调用其父类的默认构造函数,除非您在初始化列表中明确地告诉它。
B::B( int something ) {}
将隐式调用A::A()
,如果A
没有默认构造函数,则无法编译。
B::B(int something)
{
A::A(something)
}
这样做是因为它试图在初始化列表中隐式调用A::A()
,然后调用A::A(something)
,因为你知道它不会编译,因为A
没有默认构造函数。
所以如果你想调用不同的父构造函数,那么唯一的方法就是在初始化列表中进行调用
B::B(int something) : A(something) {}
答案 2 :(得分:1)
A::A(int something)
{
printf("Something = %d\n", something);
}
这是B的构造函数。构造函数所做的第一件事是构造它的基类(按类中声明的顺序,而不是构造函数的顺序),然后构造成员对象(按照声明的顺序)在类中,而不是在构造函数的顺序),然后它执行主体({}
中的代码)。这样做的原因是因为B
是A
个对象,所以它必须是完整的A
才能开始成为B
1}}对象。并且在执行任何成员函数的代码之前必须完全构造所有成员,否则可能发生不好的事情。因此,必须在构造函数体开始之前构造基类和成员。
如果要更改基类或成员的初始化方式(例如,传递整数而不是默认构造),可以将其放在初始化列表中:
B::B(int something) : A(something)
{
int x = 5;
printf("x = %d", x);
}
您不必限定A
的构造函数的名称,因为我们已经在对象B
的上下文中,并且B
已经知道A
1}}。
B::B(int something)
{
A::A(something)
}
此代码无效,因为B
会在A
中执行正文之前构建它的{}
对象。由于已经构造了A
,因此在正文中调用A::A
是没有意义的,编译器将对此进行诊断。
B::B(int something) : A(something)
{
int x = 5;
printf("x = %d", x);
}
如前所述,在构造B时,它首先构造基类,然后构造成员,然后执行主体。因此你会看到
something = 7
x = 5