C++ 类的作用域以及继承的特殊关系
- 名字遮蔽与类的作用域
- 继承的特殊关系
名字遮蔽与类的作用域
如果派生类
中的成员(包括成员变量和成员函数)和基类
中的成员重名
,通过派生类对象或者在派生类的成员函数中使用该成员时,将使用派生类新增的
成员,而不是基类的。
注意:基类
的成员函数和派生类
的成员函数不会构成重载
。如果派生类有同名函数
,那么就会遮蔽
基类中的所有
同名函数。
类是一种作用域
,每个类都有它自己的作用域
,在这个作用域
之内定义成员。
在类的作用域之外,普通的成员只能通过对象(可以是对象本身,也可以是对象指针或对象引用)来访问,静态成员
可以通过对象
访问,也可
通过类
访问。
在成员名前面
加类名
和域解析符
可以访问对象
的成员。
如果不存在
继承关系,类名
和域解析符
可省略
不写。
代码如下:
#include <iostream>
using namespace std;
class A
{
public:
int a_ = 123;
int b_ = 456;
void func() {cout << "调用 A 的 func() 函数" << endl;}
void func(int n) {cout << "调用 A 的 func(int n) 函数" << endl;}
};
int main() {
A a;
cout << "a 的成员 a_ = " << a.a_ << endl;
cout << "a 的成员 b_ = " << a.A::b_ << endl;
a.func();
a.A::func(1);
return 0;
}
编译运行结果如下:
a 的成员 a_ = 123
a 的成员 b_ = 456
调用 A 的 func() 函数
调用 A 的 func(int n) 函数
当存在继承
关系时,基类
的作用域嵌套
在派生类
的作用域中。如果成员
在派生类
的作用域
中已经找到,就不会
在基类
作用域中继续查找;如果没有找到
,则继续
在基类
作用域中查找。
如果在成员的前面加上类名
和域解析符
,就可以直接使用
该作用域的成员。
示例代码如下:
#include <iostream>
using namespace std;
class A // 基类
{
public:
int a_ = 111;
void func() {cout << "调用基类 A 的 func() 函数" << endl;}
};
class B:public A // 子类
{
public:
int a_ = 222;
void func() {cout << "调用子类 B 的 func() 函数" << endl;}
};
class C:public B // 孙类
{
public:
int a_ = 333;
void func() {cout << "调用孙类 C 的 func() 函数" << endl;}
void show() {
cout << "C::a_ = " << C::a_ << endl;
cout << "B::a_ = " << B::a_ << endl;
cout << "A::a_ = " << A::a_ << endl;
}
};
int main() {
C c;
c.show();
c.C::func();
c.B::func();
c.A::func();
return 0;
}
编译运行结果如下:
C::a_ = 333
B::a_ = 222
A::a_ = 111
调用孙类 C 的 func() 函数
调用子类 B 的 func() 函数
调用基类 A 的 func() 函数
继承的特殊关系
派生类和基类之间有一些特殊关系。
- 如果继承方式是公有的,派生类对象可以使用基类成员。
- 可以把派生类对象
复制
给基类对象(包括私有成员),但是,会舍弃
非基类的成员。
#include <iostream>
using namespace std;
class A {
public:
int a_ = 0;
private:
int b_ = 0;
public:
void show() {cout << "A::show() a = " << a_ << ", b = " << b_ << endl; }
void setb(int b) {b_ = b;}
};
class B : public A {
public:
int c_ = 0;
void show() {
cout << "B::show() a = " << a_ << " c = " << c_ << endl;
}
};
int main() {
A a;
B b;
b.a_ = 1;
b.setb(2); // 通过公有成员函数访问修改基类私有成员
b.c_ = 3;
a.show();
a = b; // 派生类对象b直接赋值给基类对象a
a.show();
return 0;
}
编译运行结果如下:
A::show() a = 0, b = 0
A::show() a = 1, b = 2
- 基类指针可以在不进行显式转换的情况下指向派生类对象。
示例代码如下:
#include <iostream>
using namespace std;
class A {
public:
int a_ = 0;
private:
int b_ = 0;
public:
void show() {cout << "A::show() a = " << a_ << ", b = " << b_ << endl; }
void setb(int b) {b_ = b;}
};
class B : public A {
public:
int c_ = 0;
void show() {
cout << "B::show() a = " << a_ << " c = " << c_ << endl;
}
};
int main() {
B b;
A *a = &b; // 声明类A的指针,让它指向b
b.a_ = 1;
b.setb(2); // 通过公有成员函数访问修改基类私有成员
b.c_ = 3;
b.show();
// 在c++中,数据类型决定了操作数据的方法。
// 现在a是A类的指针,在这个程序中,不管a指向谁,只会按A类的方法来操作数据。
a->a_ = 111;
a->setb(222);
// a->c_ = 333; // 基类A没有c_这个成员 代码错误
a->show();
return 0;
}
编译运行的结果如下:
B::show() a = 1 c = 3
A::show() a = 111, b = 222
- 基类引用可以在不进行显式转换的情况下引用派生类对象。
- 基类指针或引用只能调用基类的方法,不能调用派生类的方法。
- 可以用派生类构造基类。
- 如果函数的形参是基类,实参可以用派生类。
- C++要求指针和引用类型与赋给的类型匹配,这一规则对继承来说是例外。但是,这种例外只是单向的,不可以将基类对象和地址赋给派生类引用和指针。
感谢浏览,一起学习!