目录
1.继承
继承的好处:减少代码重复
语法:class 子类:继承方式 父类
子类(派生类),父类(基类)
代码如下:
#include<iostream>
using namespace std;
//公共
class Basepage{
public:
void header()
{
cout << "首页、公开课、登录、注册...(公共头部)" << endl;
}
void footer()
{
cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
}
void left()
{
cout << "Java、Python、C++...(公共分类列表)" << endl;
}
};
//继承
class Paython :public Basepage{
public:
void content()
{
cout << "Paython学习视频"<< endl;
}
};
class Java:public Basepage{
public:
void content()
{
cout << "Java学习视频" << endl;
}
};
class CPP:public Basepage{
public:
void content()
{
cout << "C++学习视频" << endl;
}
};
void test()
{
cout << "-----------网页内容如下:---------------" << endl;
cout << "Java部分如下:" << endl;
Java ja;
ja.header();
ja.left();
ja.content();
ja.footer();
cout << "----------------------------------------" << endl;
cout << "Paython部分如下:" << endl;
Paython pa;
pa.header();
pa.left();
pa.content();
pa.footer();
cout << "----------------------------------------" << endl;
cout << "C++部分如下:" << endl;
CPP c;
c.header();
c.left();
c.content();
c.footer();
cout << "----------------------------------------" << endl;
}
int main()
{
test();
system("pause");
return 0;
}
1.1.2继承方式
- 公共继承
- 保护继承
- 私有继承
#include<iostream>
using namespace std;
class base
{
public:
int m_a;
protected:
int m_b;
private:
int m_c;
};
//继承方式为公共继承
class son1 :public base{
public:
void func()
{
m_a = 10;//父亲中的公共权限成员,子类依然为公共权限
m_b = 10;//父亲中的保护权限成员,子类依然为保护权限
m_c = 10;//父亲中的私有成员,子类访问不到
}
};
//继承方式为保护继承
class son2 :protected base{
public:
void func()
{
m_a = 10;//父亲中的公共权限成员,子类依然为保护权限
m_b = 10;//父亲中的保护权限成员,子类依然为保护权限
m_c = 10;//父亲中的私有成员,子类访问不到
}
};
//继承方式为私有继承
class son3 :private base{
public:
void func()
{
m_a = 10;//父亲中的公共权限成员,子类依然为私有权限
m_b = 10;//父亲中的保护权限成员,子类依然为私有权限
m_c = 10;//父亲中的私有成员,子类访问不到
}
};
void text()
{
son1 s1;
s1.m_a = 100;//在son1中为公共权限
s1.m_b = 100;//在son1中为保护权限
son2 s2;
s2.m_a = 100;//在son2中为保护权限,因此类外不可以访问
s1.m_b = 100;//在son2中为保护权限
son3 s3;
s3.m_a = 100;//在son3中为私有权限
s3.m_b = 100;//在son3中为私有权限
}
int main()
{
text();
getchar();
getchar();
return 0;
}
1.1.3继承中的对象模型
class base
{
public:
int m_a;
protected:
int m_b;
private:
int m_c;
};
class son :public base{
public:
int m_d;
};
int main()
{
cout << sizeof(son) << endl;
getchar();
getchar();
return 0;
}
思考上面代码的运行结果是?
答案是16
因为父辈中的所有非静态成员属性都会被子继承
父辈中的成员属性是被编译器给隐藏了,因此访问不到,但是继承了
利用开发人员命令提示工具查看对象模型:
跳转 “F:+回车”
跳转文件路径“cd 具体路径+回车”
可以+dir查看当前文件目录
报告单个类的布局“cl /d1 reporSingleClassLayout类名 项目名”
1.1.4继承中构造与析构的顺序
创建一个son s;也会创建一个父对象(因为继承)
构造:先构造父亲,再构造儿子
析构:先析构儿子,再析构父亲
1.1.5继承同名成员处理方式
1.同名的数据成员
直接s1.m_a访问的是son里面的m_a,那么如果想要访问base里面的m_a应该怎么访问?
#include<iostream>
using namespace std;
class base
{
public:
int m_a=100;
};
class son :public base{
public:
int m_a = 200;
};
int main()
{
son s1;
cout << "son下的m_a:"<<s1.m_a << endl;
cout << "base下的m_a:" << s1.base::m_a << endl;
getchar();
getchar();
return 0;
}
如上,要想访问base(父辈)里面的m_a需要加作用域eg: s1.base::m_a;
2.同名的成员函数
和同名的数据成员一样
注意!!!:如果子类出现和父类同名的成员函数,子类的同名函数会隐藏掉父辈所有同名的成员函数,如果想要访问父辈中被隐藏的同名成员函数,则需要加作用域eg:s1.base::fun(100);
1.1.6继承同名静态成员处理方式(同同名的成员函数)
- 1.通过对象访问
- 2.通过类名访问
其中:son::base::fun();//第一个::代表通过类名访问,第二个::代表base作用域下
代码如下:
#include<iostream>
using namespace std;
class base
{
public:
int m_a=100;
static void fun()
{
cout << "base-static fun()"<< endl;
}
};
class son :public base{
public:
int m_a = 200;
static void fun()
{
cout << "son-static fun()" << endl;
}
};
int main()
{
son s1;
cout << "son下的m_a:"<<s1.m_a << endl;
cout << "base下的m_a:" << s1.base::m_a << endl;
//通过对象访问
s1.fun();
s1.base::fun();
//通过类名访问
son::fun();
son::base::fun();//第一个::代表通过类名访问,第二个::代表base作用域下
getchar();
getchar();
return 0;
}
1.1.7多继承语法
也就是c++中允许一个儿子认多个爹,但是不建议在实际开发中使用(多继承可能会出现同名问题,需要加作用域来区分)
语法:class 子类:继承方式 父类1,继承方式 父类2......
1.1.8菱形继承
如图所示:
问题:羊驼继承自动物的数据继承了两份,其实这份数据我们只需要一份。
菱形继承,两个父类继承了相同的数据,所以需要加作用域来区分
#include<iostream>
using namespace std;
class animal{
public:
int m_age;
};
class sheep :public animal{};
class tuo :public animal{};
class sheeptuo :public sheep, public tuo{};
int main()
{
sheeptuo s1;
//s1.m_age = 18;不明确,因此要加作用域进行区分
s1.sheep::m_age = 18;
s1.tuo::m_age = 20;
cout << s1.sheep::m_age << endl;
cout << s1.tuo::m_age << endl;
getchar();
getchar();
return 0;
}
代码如上,那么sheeptuo里的age应该是18岁还是20岁呢?
我们该如何解决菱形继承的这种问题呢?---------虚继承
修改如下:
class sheep :virtual public animal{};
class tuo :virtual public animal{};
其中:animal类称为虚基类,而加了virtual,继承方式就变成了虚继承
输出结果都为20
2.多态
分为动态多态和静态多态
二者区别:
静态多态函数地址早确定--编译阶段确定地址
动态多态函数地址晚确定--执行阶段确定地址
动态多态满足的条件:
- 有继承关系
- 子类重写父类的虚函数
重写:函数返回值 函数名 参数列表完全一致
使用:父类的指针或者引用指向子类的对象
eg:以下就为静态多态,地址早确定,所以dospeak不管传入什么都会输入动物在叫:
#include<iostream>
using namespace std;
class Animal{
public:
void speak()
{
cout << "动物在叫"<< endl;
}
};
class Dog:public Animal
{
public:
void speak()
{
cout << "小狗在叫"<< endl;
}
};
class Cat:public Animal
{
public:
void speak()
{
cout << "小猫在叫" << endl;
}
};
//说话函数执行
void dospeak(Animal &animal)
{
animal.speak();
}
void text()
{
Dog dog;
dospeak(dog);
}
int main()
{
text();
getchar();
getchar();
return 0;
}
我们应该怎么修改成输入dog就是小狗在叫,输入cat就是小猫在叫?-----动态多态
修改如下:
class Animal{
public:
virtual void speak()
{
cout << "动物在叫"<< endl;
}
};
空类的sizeof大小是1,修改之后sizeof大小变为4(指针 32位--指针占4个字节,64位指针占8个字节)
vfptr--虚函数(表)指针(v--virtual,f--function,ptr--pointer)---->vftable(虚函数表)表内记录虚函数的地址
当子类重写父类的虚函数,子类中的虚函数会替换子类表中记录的虚函数地址,当父类的指针或者引用指向子类的对象,发生多态eg:Animal &animal=cat;animal.speak();
纯虚函数和抽象类
多态中,通常父类的虚函数是毫无意义的,实现主要都是调用子类的重写内容,因此可以将虚函数改为纯虚函数。而类当中有了纯虚函数/纯虚析构,这个类就称为抽象类(无法实例化)
语法:virtual 返回值类型 函数名 (参数列表)=0;
eg:virtual void speak() = 0;
抽象类特点:
- 无法实例化对象(即,Animal animal;×或者new Animal;×)
- 子类必须重写纯虚函数,否则也就为一个抽象类
虚析构和纯虚析构(都是为了解决子类中析构函数无法调用的问题)
当父类指针在析构的时候,不会调用子类中的析构函数,导致子类如果有堆区数据,就无法释放干净,那么该怎么解决?----虚析构,即在父类析构函数前面加上virtual
纯虚析构需要声明和实现(类外)
virtual ~类名()=0;
类名::~类名(){}
如果子类中没有堆区数据,可以不写虚析构或者纯虚析构