148-C++学习第十一弹(静态成员,多态,虚函数)

静态成员函数补充

在这里插入图片描述

声明是告诉编译器,这里有一个num的名称占用了,
定义的特点,为num开辟空间,要放数据
编译器在链接时把名字和空间绑在一起
类里面声明的静态变量,只能被这个类产生的对象使用,外部函数不能使用
而且这个静态变量是声明为私有的
在这里插入图片描述
上面这两个操作也不允许,私有只能被对象的方法访问
静态函数的特点是没有this指针,非静态函数有this指针
看下面这个情况
在这里插入图片描述
程序运行结果如下
在这里插入图片描述

通过这个程序我们发现:静态成员可以被继承,继承与静态成员,静态成员只有一个,所有对象共享(包括基类对象和派生类对象)
静态成员num在数据区

静态成员分裂般的情况

给出一个模板
template< class T >
导致类里的成员函数也是一个模板
类外初始化静态成员需这样做
在这里插入图片描述
告诉编译器,这个num是在这个模板类里面声明的
如果在类内有一个方法,类内声明,类外定义的话
仍然是一个模板类型
在这里插入图片描述
模板类型的成员函数可以直接在类里面实现,如果要在类外实现,必须加模板,object<>里的T加进去
在这里插入图片描述
如果类里有静态函数,在类内声明,类外定义
必须给模板
在这里插入图片描述
声明的时候给static,定义的时候不加static

template<class T>
class Object
{
private:
	int value;
	static int num;	// 声明
public:
	Object(int x = 0) :value(x) { cout << "Object: " << ++num << endl; }
	~Object() { cout << "Object " << num-- << endl; }
	int GetValue()const ;
	static void Print();
};
template<class T>
int Object<T>::num = 0; // 定义

template<class T>
int Object<T>::GetValue() const { return value; }

template<class T>
void Object<T>::Print() { cout << num << end; }


class Base : public Object<Base>
{
public:
	Base(int x) :Object(x) 
	{
		cout << "Base " << endl;
	}
	~Base() {}
};
class Test : public Object<Test>
{
public:
	Test(int x = 0) :Object(x) 
	{
		cout << "Test " << endl;
	}
	~Test() {}
};
int main()
{
	Base base1(1);
	Base base2(2);
	Base base3(3);
	Test t1(1);
	Test t2(2);
	Test t3(3);
	return 0;
}

这个情况下程序的运行结果为
在这里插入图片描述

这个精神分裂般的情况是如何产生的?

在这里插入图片描述
先复习一下模板类
有几个不同的类型,就产生几个不同的实例化代码(在编译时),由编译器实行的
在这里插入图片描述
如果说在类里定义静态num
在这里插入图片描述
在类内声明一个静态成员
在实例化的时候,不光要产生一个处理int类型的栈,还要把静态成员进行实例化
在这里插入图片描述
编译器编译的时候,把静态成员,有几个类型,分裂成几个静态变量

回到主代码中
当编译器编译的时候,发现这是个模板类型,Base继承Object(给出了具体的类型< Base >),系统产生
在这里插入图片描述
Test继承Object(< Test >),系统产生
在这里插入图片描述

当Base构建对象,里面有一个Object类型的对象,这个Object类型的T类型是Base类型,有自己的num
当Test构建对象,里面有一个Object类型的对象,这个Object类型的T类型是Test类型,有自己的num
不是同一个num哦!
编译器编译的时候对类型实例化过程,造成静态成员num精神分裂
模板类型的T可以是内置类型也可以是自己设计的类型

赋值兼容规则

在这里插入图片描述
类型只有公有继承,才体现赋值兼容规则
公有派生!
1、子对象赋值给父对象
2、子对象的地址给父指针
3、子对象初始化父引用

总是把子给父!
什么原因呢?
公有继承代表“是一个”
我们定义一个类型:人
定义一个类型:学生
学生公有继承人
给出一个人对象
给出一个学生对象,学生对象包含一个人
可以说学生是一个人。
在这里插入图片描述
所以在这里可以把子对象赋值给父对象

class Person
{
	string id;
	string name;
public:
	Person(string i, string na) :id(i), name(na) {}
	~Person() {}
};
class String
{
	char* str;
public:

};
class Student : public Person
{
	int school;
public:
	Student(string id, string na, int s) :Person(id, na), school(s)
	{}
	~Student() {}
	void Print() const {
		cout << school << endl;
	}
};
int main()
{
	Person ps("9090001", "yhping");
	Student s("090111", "yhp", 10);

	Student* sp = &s;

	sp = (Student*) &ps;
	sp->Print();

	return 0;
}

ps=s;可以把s对象给给ps,此时发生

切片情况

何为切片情况?
构建ps对象,有id号,姓名
构建s对象,由两部分构成,第一部分是school,第二部分是隐藏的父对象(id号,姓名)
当要把s对象给给ps时,不可能把s的所有空间放过去。只把隐藏的父对象给ps对象赋值。
在这里插入图片描述
以上这种情况就是切片情况
在这里插入图片描述
子对象base占8个字节 有value,num
obj对象占4个字节,有value
子对象能赋值父对象的原因是公有继承,“是一个”的概念
在物理上,内存操作上面,发生切片现象

父不是子,人不是学生,不能把父对象赋值给子对象
而且就算父给子,只能确定隐藏父对象的值,而子的值不确定

创建子对象的隐藏父对象不是ps哦

可以使用父对象的指针指向父对象,指向子对象都没有问题
在这里插入图片描述
string大小是28字节

当程序编译的过程中,给sp开辟空间,有id和姓名,这两个字符串
对象大小与字符串大小无关(字符串存在堆区)
构建s对象,3个28字节,有学校,隐藏父对象有id,姓名
定义一个人的指针,人的指针可以指向人,也可以指向学生的地址
sp指向学生的时候,只能识别隐藏的父对象这一部分。

在这里插入图片描述
当你拿父指针指向父对象,可以看见整个父,指向子对象,只能看见隐藏的父对象
在这里插入图片描述
引用子对象,也只能引用子对象的隐藏父对象
在这里插入图片描述
不能把父对象的地址给子类型的指针
进行强制转换
在这里插入图片描述
在这里插入图片描述
这个打印Print会出现什么问题呢?
产生崩溃!
我们拿string定义school
sp对象在内存中开辟空间,上面是id号,下面是姓名,我们拿子指针指向父对象的地址,sp是子类型,调动Print,编译器可以编译通过,调动方法,传递指针sp,sp指向对象,调动这个对象的school,但是这个对象没有school,字符串是在堆区开辟
在这里插入图片描述
在这里插入图片描述
如果是int school;把整型值认为是school,打印出来的就是乱码了
在这里插入图片描述
对一个意想不到的空间进行初始化
在这里插入图片描述
崩溃还是随机值取决于你定义的是内置类型还是自己设计的类型

继承关系中,构造函数,拷贝构造函数,赋值语句的重载,析构函数之间的关系

在这里插入图片描述
这里有一个注意点,endl
在这里插入图片描述

在这里插入图片描述
当我们要构建base1,给base1开辟足够的空间,大小是4+4=8字节
先调用base的构造函数。先去构建隐藏父对象,Object的构造,Object构造后然后再构造base本身,再初始化num。
先有父再有子
在这里插入图片描述
打印的地址是同一个的地址:构建base,空间大小8个字节,包含隐藏父对象,从最上开始就算子对象的地址
析构的时候先把子析构,再把父析构

如果拿base1去构建base2时
在这里插入图片描述
在这里插入图片描述
构建base1,base1的隐藏父对象,value值,拿Object(x)直接给,等于10,num=x+10=20;
给base2开辟空间(8个字节),要调动拷贝构造函数,base2的隐藏父对象的value和自己的num值各是多少呢?
调动拷贝构造函数,base合成一个拷贝构造函数,合成的拷贝构造函数会不会去调动Object的拷贝构造函数?如果不能调动,将导致value为0,num为20

运行结果是10,20,缺省拷贝构造函数会把base1地址提出来,把base2地址提出来,把base1的值按位拷贝给base2

如果是父对象给出拷贝构造函数,子对象不给出拷贝构造函数
在这里插入图片描述

在这里插入图片描述
如果是父对象不给拷贝构造函数,子对象给出拷贝构造函数
在这里插入图片描述

在这里插入图片描述
1、在继承关系中,如果父类没有写拷贝构造函数,子类型也没有写拷贝构造函数
当我们拿子对象构建另一个子对象的时候,把base1的地址抓住,把base2的地址抓住,按位拷贝,一个字节一个字节拷贝进去
2、如果写了父对象的拷贝构造函数,子对象没有写拷贝构造函数,仍然能合成一个拷贝构造函数,可以把base1内容完全拷贝给base2

3、如果子类型写了拷贝构造函数,父类型也写拷贝构造函数,拷贝构造的维护就交给程序人员,很麻烦:当base1构建base2时,调动base的拷贝构造函数,先构建父对象,系统默认调动缺省构造函数而没有调动父对象的拷贝构造函数,要明确告知,调动父类的拷贝构造函数
在这里插入图片描述
如果设计一个类型的时候,并没有动态申请空间,不需要外部资源,就不要写拷贝构造函数,系统的拷贝构造函数足以应付
如果需要外部资源,空间,就要把父子类的拷贝构造函数写进去,并且明确告知子类的拷贝构造要调动父类的拷贝构造函数

在这里插入图片描述

在这里插入图片描述
缺省赋值,把base2 和 base1的地址抓住,按位赋值

父类型写赋值语句重载,子类型不写赋值语句重载
在这里插入图片描述
在这里插入图片描述

子类型合成了一个赋值语句,可以调动父的

如果父没有赋值语句重载,子写了赋值语句重载
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
赋值都可以进行。但是父类型没有写赋值语句重载

如果父类型写了赋值语句重载,子类型写了赋值语句重载
在这里插入图片描述
必须显示说明子的赋值语句调动父的赋值语句,如果没有显示,没有办法调动父对象的赋值语句
在这里插入图片描述
如果不是继承关系,是包含关系(组合关系)
只有构造函数,析构函数
在这里插入图片描述
在这里插入图片描述
我们都有相应的构造函数,首先构造base1,先构造成员,
如果没有在这里插入图片描述
会不会构造obj对象?和 num
会!调动缺省构造函数,把obj对象构建出来,拿x赋值,要创建临时对象对obj赋值,然后销毁临时对象
如果在列表调动构造函数,构造obj的同时就初始化了
在这里插入图片描述
参数列表效率高!
在这里插入图片描述
在这里插入图片描述
如果父类型有一个拷贝构造函数,那么子类型的拷贝构造函数会不会去调用父类型的拷贝构造函数呢?
会的,合成了成员拷贝构造函数。
如果写了子的拷贝构造函数,没有写父的拷贝构造函数,能不能反向要求父的拷贝构造函数?
在这里插入图片描述

如果都写了,也不调动父的拷贝构造函数
要写
在这里插入图片描述
在这里插入图片描述
继承关系是是一个,而这种关系是由什么构成,区别在于构造

#include<iostream>
#include<string>
using namespace std;
// construct, copy construct , opeator = , deconstruct

class Object
{
	int value;
public:
	Object(int x = 0) :value(x) {
		cout << "construct object: " << this << endl;
	}
 
	Object(const Object& obj) :value(obj.value)
	{
		cout << "copy construct object: " << this << endl;
	}
  	
  /*Object& operator=(const Object& obj)
	{
		if (this != &obj)
		{
			value = obj.value;
		}
		cout << "Object = " << endl;
		return *this;

	}
	*/
	~Object()
	{
		cout << "deconstruct object: " << this << endl;
	}
};

class Base 
{
private:
	Object obj;
	int num;
public:
	Base(int x = 0) :num(x + 10), obj(x)
	{
		//obj = x;
		cout << "construct Base: " << this << endl;
	}
	~Base()
	{
		cout << "deconstruct Base: " << this << endl;
	}

	Base(const Base& base) :num(base.num) ,obj(base.obj)
	{
		cout << "copy construct Base: " << this << endl;
	}
	/*
	Base& operator=(const Base& base)
	{
		if (this != &base)
		{
			Object::operator=(base);   // 
			num = base.num;
		}
		cout << "Base = " << endl;
		return *this;
	} 
	*/
};
int main()
{
	Base base1(10);
	Base base2(base1);

	return 0;
}

class Object
{
	int value;
public:
	Object(int x = 0) :value(x) {
		cout << "construct object: " << this << endl;
	}
	Object(const Object& obj) :value(obj.value)
	{
		cout << "copy construct object: " << this << endl;
	}

	Object& operator=(const Object& obj)
	{
		if (this != &obj)
		{
			value = obj.value;
		}
		cout << "Object = " << endl;
		return *this;

	}

	~Object()
	{
		cout << "deconstruct object: " << this << endl;
	}
};
class Base : public Object
{
private:
	int num;
public:
	Base(int x = 0) :num(x + 10), Object(x)
	{
		cout << "construct Base: " << this << endl;
	}
	Base(const Base& base) :num(base.num),Object(base)	// 
	{
		cout << "copy construct Base: " << this << endl;
	}
	~Base()
	{
		cout << "deconstruct Base: " << this << endl;
	}
	Base & operator=(const Base &base)
	{
		if (this != &base)
		{
			Object::operator=(base);   // 
			num = base.num;
		}
		cout << "Base = " << endl;
		return *this;
	}
};
int main()
{
	Base base1(10);		  // sizeof(Base);	  8 bytes;

	Base base2(100);

	base1 = base2;

	return 0;
}

多态和虚函数

在这里插入图片描述

早起绑定
在这里插入图片描述
名字和地址的关联,编译时关联的,早期绑定
在这里插入图片描述
运行时多态,必须是继承,公有继承,虚函数
比喻和类推

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林林林ZEYU

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值