【c++】c++核心编程(3)

目录

1.继承

2.多态


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;

类名::~类名(){}

如果子类中没有堆区数据,可以不写虚析构或者纯虚析构

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值