1. 初始化列表
初始化列表在三种情况下必须使用:
- 继承关系下,父类没有无参构造函数情况
#include <iostream>
using namespace std;
class Base{
public:
string name;
int num;
Base(){
cout<< "基类的无参构造..." <<endl;
}
Base(string name){
cout<< "基类的有参构造1..." <<endl;
}
Base(string name,int num){
cout<< "基类的有参构造2..." <<endl;
}
};
class Derived : public Base {
public:
Derived(){
cout<< "派生类的无参构造..." <<endl;
}
Derived(string name):Base(name){
cout<< "派生类的有参构造1..." <<endl;
}
Derived(string name,int num):Base(name,num){
cout<< "派生类的有参构造2..." <<endl;
}
};
int main() {
Derived d;
cout << "------------------------" << endl;
Derived d0("zhangfei");
Derived d1("libai",24);
return 0;
}
运行结果:
基类的无参构造...
派生类的无参构造...
------------------------
基类的有参构造1...
派生类的有参构造1...
基类的有参构造2...
派生类的有参构造2...
- 需要初始化const修饰的类成员或初始化引用成员数据
#include <iostream>
using namespace std;
class person{
public:
const int ID;
int & age;
person(int no , int age):ID(no),age(age){
cout << "执行构造函数..." <<endl;
}
};
int main(){
person p(78 , 28);
cout << p.ID << " , " << p.age << endl;
return 0 ;
}
- 需要初始化的数据成员是对象,并且对应的类没有无参构造函数
#include <iostream>
using namespace std;
class Base{
public:
string name;
int num;
Base(){
cout<< "基类的无参构造..." <<endl;
}
Base(string name){
cout<< "基类的有参构造1..." <<endl;
}
Base(string name,int num){
cout<< "基类的有参构造2..." <<endl;
}
};
class Derived{
public:
int no;
Base b;
// Derived(){
// cout<< "派生类的无参构造..." <<endl;
// }
Derived(string name):b(name){
cout<< "派生类的有参构造1..." <<endl;
}
Derived(string name,int num):b(name,num){
cout<< "派生类的有参构造2..." <<endl;
}
};
int main() {
cout << "------------------------" << endl;
Derived d0("zhangfei");
cout << "------------------------" << endl;
Derived d1("libai",24);
return 0;
}
运行结果:
------------------------
基类的有参构造1...
派生类的有参构造1...
------------------------
基类的有参构造2...
派生类的有参构造2...
or
#include <iostream>
using namespace std;
class Base{
public:
string name;
int num;
Base(){
cout<< "基类的无参构造..." <<endl;
}
Base(string name){
cout<< "基类的有参构造1..." <<endl;
}
Base(string name,int num){
cout<< "基类的有参构造2..." <<endl;
}
};
class Derived{
public:
int no;
Base b;
Derived():b(){
cout<< "派生类的无参构造..." <<endl;
}
Derived(string name):b(name){
cout<< "派生类的有参构造1..." <<endl;
}
Derived(string name,int num):b(name,num){
cout<< "派生类的有参构造2..." <<endl;
}
};
int main() {
Derived d;
cout << "------------------------" << endl;
Derived d0("zhangfei");
cout << "------------------------" << endl;
Derived d1("libai",24);
return 0;
}
运行结果:
基类的无参构造...
派生类的无参构造...
------------------------
基类的有参构造1...
派生类的有参构造1...
------------------------
基类的有参构造2...
派生类的有参构造2...
2. 重写父类同名函数
在C++中,子类可以重写(override)父类的同名函数。这被称为函数的覆盖(function overriding)。当子类重写父类的函数时,它必须具有相同的名称、参数列表和返回类型。
- 特点:
- 使用场景:
#include <iostream>
// 父类
class Shape {
public:
virtual void draw() {
std::cout << "绘制形状" << std::endl;
}
};
// 子类 Circle
class Circle : public Shape {
public:
void draw() override { // 使用 override 关键字表示重写
std::cout << "绘制圆形" << std::endl;
}
};
// 子类 Rectangle
class Rectangle : public Shape {
public:
void draw() override {
std::cout << "绘制矩形" << std::endl;
}
};
int main() {
Shape* shape1 = new Circle();
shape1->draw(); // 输出:绘制圆形
Shape* shape2 = new Rectangle();
shape2->draw(); // 输出:绘制矩形
delete shape1;
delete shape2;
return 0;
}
3. 多继承
在C++中,子类多继承是指一个派生类从多个基类继承特性和行为。这意味着一个子类可以同时拥有多个父类的成员和方法。
- 特点
- 使用场景
- 代码实现
#include <iostream>
// 基类A
class A {
public:
void funcA() {
std::cout << "This is function A." << std::endl;
}
};
// 基类B
class B {
public:
void funcB() {
std::cout << "This is function B." << std::endl;
}
};
// 子类C从基类A和基类B进行多继承
class C : public A, public B {
public:
void funcC() {
std::cout << "This is function C." << std::endl;
}
};
int main() {
C c;
c.funcA(); // 调用基类A的函数
c.funcB(); // 调用基类B的函数
c.funcC(); // 调用子类C自身的函数
return 0;
}
3.1 多继承的构造函数
多继承形式下的构造函数和单继承形式基本相同,只是要在子类的构造函数中调用多个父类的构造函数 。 他们调用的顺序由定义子类时,继承顺序 决定。
#include <iostream>
using namespace std;
// 基类A
class A {
public:
A(){
cout<< "A类的无参构造..." <<endl;
}
~A(){
cout<< "A类的析构构造..." <<endl;
}
};
// 基类B
class B {
public:
B(){
cout<< "B类的无参构造..." <<endl;
}
~B(){
cout<< "B类的析构构造..." <<endl;
}
};
// 子类C从基类A和基类B进行多继承
class C : public A, public B {
public:
C(){
cout<< "C类的无参构造..." <<endl;
}
~C(){
cout<< "C类的析构构造..." <<endl;
}
};
int main() {
C c;
return 0;
}
运行结果:
A类的无参构造...
B类的无参构造...
C类的无参构造...
C类的析构构造...
B类的析构构造...
A类的析构构造...
4. 类的前置声明
C++中的类前置声明是指在使用类之前提前声明类的存在,而不需要完整地定义类。它可以在某些场景下用于解决循环依赖或减少编译时间的问题。
- 特点:
- 使用场景
- 解决循环依赖
当两个或多个类相互引用时,可以使用前置声明来解决循环依赖的问题。通过提前声明类的存在,可以在类定义之前使用该类的指针或引用。
- 注意:
#include <iostream>
class B; // 前置声明
class A {
private:
B* b;
public:
void setB(B* obj) {
b = obj;
}
void doSomething();
};
class B {
private:
A* a;
public:
void setA(A* obj) {
a = obj;
}
void doSomething();
};
void A::doSomething() {
std::cout << "a->doSomething()" << std::endl;
if (b) {
b->doSomething();
}
}
void B::doSomething() {
std::cout << "b->doSomething()" << std::endl;
if (a) {
a->doSomething();
}
}
int main() {
A a;
B b;
a.setB(&b);
b.setA(&a);
std::cout << "-----------------------------" << std::endl;
a.doSomething();
b.doSomething();
return 0;
}
类A和类B相互引用,并使用了前置声明。在类A中,成员变量B* b是一个指针类型,不会执行B类的无参构造函数,因此不需要知道B类是否有无参构造函数。而如果将成员变量声明为B& b,则是一个引用类型,也不会执行B类的无参构造函数。
在类定义之后,我们定义了类A和类B的成员函数doSomething()。在A::doSomething()中,调用了b->doSomething(),即调用了B类的成员函数。同样,在B::doSomething()中,调用了a->doSomething(),即调用了A类的成员函数。
在main()函数中,我们创建了类A和类B的对象,并通过setB()和setA()方法设置了它们之间的循环依赖关系。最后,调用了a.doSomething()和b.doSomething()来触发成员函数的调用。
- 提高编译速度
在一些情况下,完整的类定义可能不是必需的,例如在函数声明中只需要使用类的指针或引用,而不需要访问类的成员。这时,使用前置声明可以减少编译时间,因为编译器不需要包含和处理完整的类定义。
#include <iostream>
// 前置声明
class SomeClass;
void useSomeClass(SomeClass* obj);
int main() {
SomeClass* obj = new SomeClass();
useSomeClass(obj);
delete obj;
return 0;
}
// 完整类定义
class SomeClass {
public:
void doSomething();
};
void useSomeClass(SomeClass* obj) {
obj->doSomething();
}
void SomeClass::doSomething() {
std::cout << "Doing something..." << std::endl;
}
5. 继承的使用场景
- 代码重用和封装:当多个类具有相似的属性和行为时,可以将这些共同特征提取到一个基类中,派生类继承基类以获得这些共同特征,并在派生类中添加额外的特定功能。
class Animal {
public:
void eat() {
std::cout << "Animal is eating." << std::endl;
}
};
class Dog : public Animal {
public:
void bark() {
std::cout << "Dog is barking." << std::endl;
}
};
int main() {
Dog dog;
dog.eat(); // 继承自Animal类
dog.bark(); // Dog类自身的函数
return 0;
}
- 实现多态性:通过基类的指针或引用,可以以统一的方式操作不同的派生类对象,实现多态性。这样可以在运行时动态地选择调用不同派生类的特定函数。
class Shape {
public:
virtual void draw() {
std::cout << "Drawing a shape." << std::endl;
}
};
class Circle : public Shape {
public:
void draw() override {
std::cout << "Drawing a circle." << std::endl;
}
};
class Rectangle : public Shape {
public:
void draw() override {
std::cout << "Drawing a rectangle." << std::endl;
}
};
int main() {
Shape* shape1 = new Circle();
Shape* shape2 = new Rectangle();
shape1->draw(); // 动态调用Circle类的draw函数
shape2->draw(); // 动态调用Rectangle类的draw函数
delete shape1;
delete shape2;
return 0;
}
- 扩展和特化功能:通过继承,可以在派生类中添加额外的成员变量和成员函数,以实现对基类功能的扩展和特化。
class Vehicle {
protected:
int wheels;
public:
Vehicle(int numWheels) : wheels(numWheels) {}
void printWheels() {
std::cout << "Number of wheels: " << wheels << std::endl;
}
};
class Car : public Vehicle {
public:
Car() : Vehicle(4) {}
void startEngine() {
std::cout << "Engine started." << std::endl;
}
};
int main() {
Car car;
car.printWheels(); // 继承自Vehicle类
car.startEngine(); // Car类自身的函数
return 0;
}