类继承
一、继承的基本概念
被继承的类称为基类或父类,继承的类称为派生类或子类。
就是子类从父类中得到成员的一个过程!
#include <iostream>
using namespace std;
class Father {
public:
int m_age;
Father() {
m_age=18;
cout<<"use Father's func\n";
}
~Father() {
cout<<"use Father's xigou\n";
}
};
class Son:public Father {
public:
int m_xw;
Son() {
m_xw=99;
cout<<"use Son's func\n";
}
~Son() {
cout<<"use Son's xigou\n";
}
};
int main() {
Son s1;
cout<<s1.m_age<<' '<<s1.m_xw<<'\n';
return 0;
}
输出,也就是子类对象:
- 调用父类构造函数
- 调用子类构造函数
- 调用子类析构函数
- 调用父类析构函数
use Father's func
use Son's func
18 99
use Son's xigou
use Father's xigou
二、继承方式
不管继承方式如何,基类中的private成员在派生类中始终不能使用,也更没办法在类外去使用。
1. 三种访问权限
class Example {
public: // 公有:类外可以访问
int public_member;
protected: // 保护:类外不能访问,但派生类可以访问
int protected_member;
private: // 私有:只能在本类中访问
int private_member;
};
2. 三种继承方式
class Base {
public:
int public_member;
protected:
int protected_member;
private:
int private_member;
};
// 公有继承
class DerivedPublic : public Base {
// public_member 保持 public
// protected_member 保持 protected
// private_member 不可访问
};
// 保护继承
class DerivedProtected : protected Base {
// public_member 变为 protected
// protected_member 保持 protected
// private_member 不可访问
};
// 私有继承
class DerivedPrivate : private Base {
// public_member 变为 private
// protected_member 变为 private
// private_member 不可访问
};
3. 例子
在原始继承完了之后,可以用 using 去改变权限。
#include <iostream>
using namespace std;
class A { // 基类
public:
int m_a = 10; // 公有成员
protected:
int m_b = 20; // 保护成员
private:
int m_c = 30; // 私有成员
};
class B : public A { // 公有继承
public:
using A::m_b; // 把 m_b 的权限从 protected 改为 public
private:
using A::m_a; // 把 m_a 的权限从 public 改为 private
};
int main() {
B b;
// b.m_a = 11; // ❌ 错误:m_a 在 B 中变为 private,类外不能访问
b.m_b = 21; // ✅ 正确:m_b 被 using 改为 public,可以访问
// b.m_c = 31; // ❌ 错误:m_c 在 A 中是 private,派生类永远不能访问
}
三、类继承的对象模型
其实关于这个继承,我们完全可以把子类跟父类分开来,变成两个独立的类,只不过父类中有的成员子类中也有而已!
创建派生类对象时,程序首先调用基类构造函数,然后再调用派生类构造函数。
四、名字遮蔽
就是从下到上,找到同名的就 over!
名字一样就好了!
当派生类定义了与基类同名的成员(变量或函数)时,派生类的成员会遮蔽基类的同名成员。
class Base {
public:
void show() { cout << "Base::show()" << endl; }
};
class Derived : public Base {
public:
void show() { cout << "Derived::show()" << endl; } // 遮蔽了 Base::show()
};
int main() {
Derived d;
d.show(); // 输出:Derived::show(),而不是 Base::show()
// 即使 Base::show() 存在,也被 Derived::show() 遮蔽了
}
class A { public: int x = 1; };
class B : public A { public: int x = 2; }; // 遮蔽 A::x
class C : public B { public: int x = 3; }; // 遮蔽 B::x
int main() {
C obj;
cout << obj.x << endl; // 输出3,先在C中找到x
cout << obj.B::x << endl; // 输出2,明确指定B的作用域
cout << obj.A::x << endl; // 错误!不能直接访问A::x
cout << obj.B::A::x << endl; // 输出1,通过B访问A
}
函数遮蔽 vs 函数重载
重要区别:函数在不在一块!
函数重载:在同一作用域中,函数名相同但参数不同
函数遮蔽:在不同作用域(基类和派生类)中,函数名相同(无论参数是否相同)
class Base {
public:
void func() { cout << "Base::func()" << endl; }
void func(int) { cout << "Base::func(int)" << endl; } // 重载
};
class Derived : public Base {
public:
void func() { cout << "Derived::func()" << endl; } // 遮蔽了Base的所有func!
};
int main() {
Derived d;
d.func(); // ✅ 正确:调用Derived::func()
// d.func(5); // ❌ 错误!Base::func(int)被遮蔽了
// 必须显式指定作用域
d.Base::func(); // ✅ 调用Base::func()
d.Base::func(5); // ✅ 调用Base::func(int)
}
解决
使用作用域解析符 ::
class A { public: void f() { } };
class B : public A {
public:
void f() { } // 遮蔽A::f()
void test() {
f(); // 调用B::f()
A::f(); // 调用A::f()
}
};
使用 using声明引入基类成员
class Base {
public:
void func() { }
void func(int) { }
};
class Derived : public Base {
public:
using Base::func; // 引入Base的所有func
void func(double) { } // 现在与Base::func构成重载
};
int main() {
Derived d;
d.func(); // ✅ 调用Base::func()
d.func(5); // ✅ 调用Base::func(int)
d.func(3.14); // ✅ 调用Derived::func(double)
}
继承的特殊关系
把基类看成动物,派生类看成猫、狗。
动物有的特征,猫、狗都会有!
1)如果继承方式是公有的,派生类对象可以使用基类成员。
2)可以把派生类对象赋值给基类对象(包括私有成员),但是,会舍弃非基类的成员。
3)基类指针可以在不进行显式转换的情况下指向派生类对象。(一个指向动物的指针,当然可以指向猫和狗啦)
4)基类引用可以在不进行显式转换的情况下引用派生类对象。(一个引用动物,当然也可以引用猫和狗)
基类指针或引用只能调用基类的方法,不能调用派生类的方法。因为它压根不知道有新东西!

浙公网安备 33010602011771号