#include <iostream>
using namespace std;
class Base {
private:
int x;
int y;
public:
Base(int x, int y) {
this->x = x;
this->y = y;
}
int getX() {
return x;
}
int getY() {
return y;
}
};
class Sub : public Base {
private:
int z;
public:
Sub(int x, int y, int z) : Base(x, y) {
// write your code here
this->z = z;
}
int getZ() {
return z;
}
int calculate() {
return Base::getX() * Base::getY() * this->getZ();
}
};
int main() {
int x, y, z;
cin >> x;
cin >> y;
cin >> z;
Sub sub(x, y, z);
cout << sub.calculate() << endl;
return 0;
}
"::"是作用域限定符,作用是指定函数或者变量所属的类
Base::getX() 表示:明确调用「Base 类中定义的 getX () 成员函数」,而不是子类 Sub 自己的
核心作用是避免命名冲突!
---------------------------------
一、先讲核心1:子类如何调用父类构造函数(为什么?怎么调?)
1. 为什么子类必须调用父类构造?
C++ 中子类继承了父类的成员(比如 Base 的 x、y),但子类不能直接初始化父类的私有成员(x、y 是 Base 的 private)。因此:
- 创建子类对象时,必须先初始化“父类部分的成员”,再初始化子类自己的成员;
- 父类
Base只有带参构造函数(Base(int x, int y)),没有无参的默认构造函数——如果子类不主动调用父类构造,编译器不知道怎么初始化 x、y,会直接报错。
简单说:子类构造的“第一步”必须是初始化父类,这是继承的语法规则。
2. 子类调用父类构造的语法(核心是「初始化列表」)
你的代码中:
Sub(int x, int y, int z) : Base(x, y) { // 初始化列表中调用父类构造
this->z = z;
}
: Base(x, y):这是子类构造函数的「初始化列表」,作用是显式调用父类 Base 的带参构造函数,把参数x、y传给父类,让父类初始化自己的私有成员x、y;- 执行顺序:创建
Sub对象时,先执行Base(x, y)初始化父类的 x、y → 再执行子类构造函数体this->z = z初始化子类自己的 z。
3. 具体执行流程(结合 main 函数)
假设 main 中输入 x=2, y=3, z=4:
- 执行
Sub sub(2,3,4),触发子类Sub的构造函数; - 先执行初始化列表
Base(2,3):调用父类构造,把父类的x=2、y=3; - 再执行子类构造函数体
this->z = 4:初始化子类的z=4; - 至此,
sub对象的完整数据:父类部分x=2、y=3,子类部分z=4。
补充:如果父类有默认构造,子类可以不显式调用
如果 Base 有无参构造:
Base() { x=0; y=0; } // 父类默认构造
那么子类构造可以简化为:
Sub(int x, int y, int z) { // 无需初始化列表调用Base,编译器自动调用默认构造
this->z = z;
}
但你的代码中父类没有默认构造,所以必须显式调用。
二、再讲核心2:Base::getX() 是什么意思?为什么要这样写?
1. :: 是「作用域限定符」,核心作用是“指定函数/变量所属的类”
Base::getX() 表示:明确调用「Base 类中定义的 getX() 成员函数」,而不是子类 Sub 自己的(如果有的话)。
2. 为什么要这样写?(3个核心原因)
原因1:避免“命名冲突”(最关键)
如果子类 Sub 也定义了同名的 getX() 函数(比如):
class Sub : public Base {
public:
// 子类重定义了getX()
int getX() {
return 0;
}
// ...其他代码
int calculate() {
// 此时:getX() → 调用子类自己的getX()(返回0);Base::getX() → 调用父类的getX()(返回父类x)
return getX() * Base::getY() * this->getZ();
}
};
如果不加 Base::,编译器会优先调用子类自己的 getX(),导致逻辑错误;加了 Base:: 才能精准调用父类的 getX()。
原因2:提升代码“可读性”
即使子类没有同名函数,写 Base::getX() 也能让阅读代码的人一眼看出:“这个 getX() 是从父类 Base 继承来的”,尤其在继承体系复杂时(比如多层继承),能避免歧义。
原因3:规范写法(养成好习惯)
在继承场景中,显式用 父类名:: 调用父类成员,是工业级代码的常见规范——避免后续维护时(比如给子类加同名函数),代码逻辑突然出错。
3. 补充:不加 Base:: 行不行?(你的代码中可以,但不推荐)
你的代码中,Sub 没有重定义 getX(),所以:
int calculate() {
// 不加Base::,编译器会自动找父类的getX(),结果一样
return getX() * getY() * getZ();
}
虽然能运行,但一旦后续给 Sub 加了 getX(),代码逻辑就会变,因此显式写 Base:: 更健壮。
三、完整执行流程(结合 calculate() 函数)
还是以输入 x=2, y=3, z=4 为例:
- 调用
sub.calculate(); Base::getX()→ 调用父类的 getX(),返回父类的 x=2;Base::getY()→ 调用父类的 getY(),返回父类的 y=3;this->getZ()→ 调用子类的 getZ(),返回子类的 z=4;- 计算
2 * 3 * 4 = 24,返回并输出。
四、核心总结
| 子类构造的初始化列表中调用父类构造,初始化父类继承的成员(x、y) |
| 用作用域限定符
指定调用父类的 getX(),避免命名冲突、提升可读性 |
简单记:
- 子类调父类构造:必须在初始化列表里(父类无默认构造时),先构父、后构子;
父类名::成员:精准指定用父类的成员,不怕子类重名,代码更稳。

京公网安备 11010502036488号