继承
1. 基础使用
继承就是在一个已经存在的类的基础上新建立一个类,新创建的类拥有之前类的特性。继承是面向对象的三大特性之一,体现了代码复用的思想。
- 已经存在的类被称为“基类 Base Class”或“父类”
- 新创建的类被称为“派生类”或“子类Sub Class”
下面是一个最简单的单一继承:
#include <iostream>
using namespace std;
/**
* @brief The Father class 基类
*/
class Father
{
public:
string first_name = "欧阳";
void work()
{
cout << "我是一个厨师" << endl;
}
};
/**
* @brief The Son class 派生类
*/
class Son:public Father
{
};
int main()
{
Son s;
cout << s.first_name << endl;
s.work();
return 0;
}
上面的继承在实际开发中没有任何意义,因为派生类对基类没有做出任何改变。通常派生类都会在基类的基础上做出一些代码修改或增加。
#include <iostream>
using namespace std;
/**
* @brief The Father class 基类
*/
class Father
{
public:
string first_name = "欧阳";
void work()
{
cout << "我是一个厨师" << endl;
}
};
/**
* @brief The Son class 派生类
*/
class Son:public Father
{
public:
void play() // 增加的代码
{
cout << "打游戏" << endl;
}
// 【函数隐藏】与基类的work函数签名相同
void work()
{
cout << "我要做码农" << endl;
}
};
int main()
{
Son s;
cout << s.first_name << endl;
s.work();
// 也可以调用被隐藏的基类函数
s.Father::work();
s.play();
return 0;
}
基类和派生类是相对的,一个类既可以作为基类又可以作为派生类,取决于两个类之间的关系。派生类是基类的具象化,基类是派生类的抽象化。
对于基类的私有成员,派生类可以继承,但是无法直接访问,访问需要通过基类提供的接口。
#include <iostream>
using namespace std;
class Father
{
private:
string first_name = "欧阳";
public:
string get_first_name() const
{
return first_name;
}
void set_first_name(string fn)
{
first_name = fn;
}
};
class Son:public Father
{
public:
Son()
{
// cout << first_name << endl; 错误
}
};
class Test
{
};
int main()
{
Son s;
// cout << s.first_name << endl; 错误
Test t;
Father f;
cout << sizeof(f) << endl; // 4
cout << sizeof(s) << " " << sizeof(t) << endl; // 4 1
// 通过基类接口可以访问继承的基类私有成员
s.set_first_name("司马");
cout << s.get_first_name() << endl; // 司马
return 0;
}
2. 构造函数
派生类的构造函数必须直接或间接调用基类的任意一个构造函数。如果程序员不手动在派生类的构造函数中调用基类的构造函数,编译器会尝试调用基类的无参构造函数。
#include <iostream>
using namespace std;
class Father
{
public:
Father()
{
cout << "基类的构造函数" << endl;
}
};
class Son:public Father
{
public:
// 编译器增加以下代码
Son():Father() // 透传构造:在派生类的构造函数中调用基类的构造函数
{
}
};
int main()
{
Son s;
return 0;
}
编译器无法处理所有情况,例如基类没有无参构造函数。
#include <iostream>
using namespace std;
class Father
{
private:
string name;
public:
Father(string name)
{
this->name = name;
cout << "基类的构造函数" << endl;
}
void set_name(string name)
{
this->name = name;
}
string get_name() const
{
return name;
}
};
class Son:public Father
{
public:
// 编译器只会自动调用基类的无参构造
// 基类现在没有无参构造
// 报错!
};
int main()
{
Son s;
return 0;
}
上面的情况必须程序员手动在派生类的构造函数中直接或间接调用基类的构造函数:
- 透传构造
- 委托构造
- 继承构造(C++11)
注:构造函数和析构函数是不能被继承的,继承构造仅仅是一种用法的名称,并没有继承构造函数。
2.1 透传构造
透传构造属于在派生类的构造函数中直接调用基类的构造函数。
#include <iostream>
using namespace std;
class Father
{
private:
string name;
int age;
public:
Father(string name)
{
this->name = name;
age = 1;
}
Father(string name,int age)
:name(name),age(age){}
void show()
{
cout << name << " " << age << endl;
}
};
class Son:public Father
{
public:
Son():Father("佚名") // 通过透传构造调用基类的一参构造函数
{
}
Son(string name,int age):
Father(name,age) // 通过透传构造调用基类的二参构造函数
{}
};
int main()
{
Son s;
s.show();
Son s1("李白",29);
s1.show();
return 0;
}
2.2 委托构造
委托构造本身可以脱离继承使用,指的是在某个类中构造函数A可以调用构造函数B。在继承中就可以让构造函数A调用构造函数B,构造函数B透传调用基类的构造函数,这样构造函数A就间接调用了基类的构造函数。
#include <iostream>
using namespace std;
class Father
{
private:
string name;
int age;
public:
Father(string name)
{
this->name = name;
age = 1;
}
Father(string name,int age)
:name(name),age(age){}
void show()
{
cout << name << " " << age << endl;
}
};
class Son:public Father
{
public:
// 构造函数A委托调用构造函数B(Son的二参构造函数)
Son():Son("佚名",2)
{
}
// 构造函数B
Son(string name,int age):
Father(name,age) // 通过透传构造调用基类的二参构造函数
{}
};
int main()
{
Son s;
s.show();
Son s1("李白",29);
s1.show();
return 0;
}
委托构造要避免委托闭环。
#include <iostream>
using namespace std;
class Father
{
private:
string name;
int age;
public:
Father(string name)
{
this->name = name;
age = 1;
}
Father(string name,int age)
:name(name),age(age){}
void show()
{
cout << name << " " << age << endl;
}
};
class Son:public Father
{
public:
// 构造函数A委托调用构造函数B(Son的二参构造函数)
Son():Son("佚名",2)
{
}
// 构造函数B委托调用构造函数A
Son(string name,int age):
Son()
{}
};
int main()
{
Son s;
s.show();
Son s1("李白",29);
s1.show();
return 0;
}
在上面代码中,程序会卡在第46行,因为构造函数执行不完,互相委托形成闭环。