文章目录
一、重载、重写(覆盖)与隐藏的区别
-
重载是指函数名相同,但是函数参数类型或者参数个数不同的一类函数,返回值不能作为重载的标志。
-
重写(覆盖)是指派生类中重新定义的虚函数,函数名、参数列表和函数返回值都和基类相同。
-
隐藏是指派生类中定义了和基类同名的函数,只要同名就会被隐藏。
二、私有继承、公有继承、保护继承
三、多重继承与虚继承
1、多重继承
多重继承是指从多个基类中产生派生类的情况,多重继承的派生类继承了所有父类的属性。
构造一个派生类对象时将会同时构造并初始化它所有的基类子对象,如若不是显示初始化,则其会使用基类的默认构造函数进行初始化,基类的构造顺序与派生类列标准基类出现的顺序一致。
C++11新标准,派生类可以从其基类中继承构造函数,但是若从所个基类中继承了相同的构造函数(形参列表完全相同的情况),就会发生错误,此时该派生类应该为它自己定义一个形参列表相同的构造函数。
析构函数执行的顺序与构造函数相反,派生类的析构函数只负责清除派生类本身分配的资源,派生类的成员及基类都是自动销毁的。
2、类型转换与多个基类
可以令某个可访问基类的指针或引用直接指向一个派生类对象,但该指针只能访问其对应的基类部分或者基类的基类部分。
当一个类拥有多个基类时,有可能从多个类中继承了同名成员的情况,此时需要加上前缀限定符,避免二义性最好的办法是在派生类中为该成员定义一个新的版本。
3、多重继承demo:
#include <iostream>
#include <string>
using namespace std;
class Worker
{
public:
Worker(string code = "wk")
{
m_strCode = code;
cout << "Worker()" << endl;
}
virtual ~Worker()
{
cout << "~Worker()" << endl;
}
void carry()
{
cout << m_strCode << endl;
cout << "Worker-carry()" << endl;
}
protected:
string m_strCode;
};
class Farmer
{
public:
Farmer(string name = "fa")
{
m_strName = name;
cout << "Farmer()" << endl;
}
virtual ~Farmer()
{
cout << "~Farmer()" << endl;
}
void sow()
{
cout << m_strName << endl;
cout << "Farmer-sow()" << endl;
}
protected:
string m_strName;
};
class MigrantWorker : public Farmer, public Worker//构造函数执行的顺序和声明的顺序一致。
{
public:
MigrantWorker(string name, string code): Worker(code), Farmer(name)//列表初始化,向下构造。
{
cout << "MigrantWorker" << endl;
}
~MigrantWorker()//向上析构。
{
cout << "~MigrantWorker()" << endl;
}
};
int main(int argc, char const *argv[])
{
MigrantWorker *p = new MigrantWorker("jack","100");
p->carry();
p->sow();
delete p;
p = nullptr;
return 0;
}
4、虚继承
虚继承的目的是令某个类做出声明,承诺愿意共享它的基类,共享的基类子对象称为及虚基类,在此情况下,无论虚基类在集成体系中出现多少次,派生类中都只包含唯一一个共享的虚基类对象。
虚继承主要解决的是菱形继承问题。对于菱形继承,使用虚继承的好处在于避免重复拷贝最上层的父类。
【Note】:
1)虚派生只影响从指定了虚基类的派生类中进一步派生出的类,它不会影响派生类本身。
2)虚基类总是先于非虚基类构造,与它们在继承体系中的次序和位置无关。
只要我们能创建虚基类的派生类对象,该派生类的构造函数就必须初始化它的虚基类。
首先用最底层派生类的构造函数的初始值初始化该对象的虚基类子部分,虚基类优先构造,析构函数执行顺序与构造函数相反。
5、虚继承demo:
#include <iostream>
#include <string>
using namespace std;
//对于菱形继承,使用虚继承的好处在于避免重复拷贝最上层的父类。
class Person
{
public:
Person(string color = "blue")
{
m_strColor = color;
cout << "Person()" << endl;
}
virtual ~Person()
{
cout << "~Person()" << endl;
}
void printColor()
{
cout << m_strColor << endl;
cout << "Person-printColor()" << endl;
}
protected:
string m_strColor;
};
class Worker : virtual public Person//虚继承。
{
public:
Worker(string code = "wk", string color = "blue") : Person(color)
{
m_strCode = code;
cout << "Worker()" << endl;
}
virtual ~Worker()
{
cout << "~Worker()" << endl;
}
protected:
string m_strCode;
};
class Farmer : virtual public Person//虚继承。
{
public:
Farmer(string name = "fa", string color = "blue") : Person(color)
{
m_strName = name;
cout << "Farmer()" << endl;
}
virtual ~Farmer()
{
cout << "~Farmer()" << endl;
}
protected:
string m_strName;
};
class MigrantWorker : public Farmer, public Worker
{
public:
MigrantWorker(string name, string code, string color):
Worker(code, color), Farmer(name, color)
{
cout << "MigrantWorker" << endl;
}
~MigrantWorker()
{
cout << "~MigrantWorker()" << endl;
}
};
int main(int argc, char const *argv[])
{
MigrantWorker *p = new MigrantWorker("jack","100", "yellow");
p->Farmer::printColor();//使用前缀限定符避免发生二义性。
p->Worker::printColor();
delete p;
p = nullptr;
return 0;
}
四、纯虚函数和抽象类
抽象基类是把某一些具有相似特征的类抽象化,形成一个新的类,比如人。当多个类之间有相似的方法和数据成员时,可以定义一个抽象基类。
含有(未经覆盖直接继承)纯虚函数的类是抽象基类,将虚函数后面加上“=0”即为纯虚函数。
1、面试题—>纯虚函数的实现原理,为什么抽象基类不能被实例化?
所有的虚函数都会被加入到虚函数表中,而纯虚函数在虚函数表中对应的vptr是空,也就是指向一个不存在的函数,而编译器不允许有调用一个不存在的函数。
2、面试题—>如何阻止一个类被实例化?
使用抽象基类或者将构造函数定义为private。
五、运算符重载与四种类型转换
1、运算符重载
Integer operator+(int value, Integer integer) {
int tmpValue = integer.m_value + value;
return Integer(tmpValue);
}
2、四种类型转换
C++的四种强制类型转换,所以C++不是类型安全的。分别为:static_cast , dynamic_cast , const_cast , reinterpret_cast。
// static_cast用于替换C语言中的强制类型转换
static_cast<type-id> (expression)
// 将基类指针转换为派生类指针
dynamic_cast<type-id> (expression)
// 转化任何内置的数据类型为其他任何的数据类型
reinpreter_cast(expression)
// 把非const类型转换为const类型,或者去掉const属性
const_cast(expression)