多态、泛型复习

1. 浅拷贝和深拷贝

#include<iostream>
using namespace std;

class Person
{
public:
	//无参构造函数
	Person();
	//有参构造函数
	Person(int age, int height)
	{
		m_age = age;
		m_heigt =new int(height) ;
	}
	//拷贝构造函数
	Person(const Person& p)
	{
		m_age = p.m_age;
		//如果不利用深拷贝在堆区创建新内存,会导致浅拷贝带来的重复释放堆区问题
		m_heigt = new int(*p.m_heigt);
	}
	//析构函数
	~Person()
	{
		if (m_heigt != NULL)
		{
			delete	m_heigt;
			m_heigt = NULL;
		}
	}

public:
	int m_age;
	int *m_heigt;

};
void test01()
{	
	Person p1(10, 160);
	Person p2(p1);
	cout << p1.m_age << "   " << *p1.m_heigt << endl;
	cout << p2.m_age << "   " << *p2.m_heigt << endl;
}


int main() {
	test01();
	system("pause");
	return 0;

};

2. 成员函数重载运算符

//重载前置++运算符
	Person& operator++()
	{	
		m_A++;
		return *this;
	}


	//后置递增
	Person operator++(int)
	{
		//先记录当时结果
		Person temp = *this;
		//再做递增
		m_A++;
		//最后将记录返回
		return temp;
	}

//重载赋值=运算符
Person& operator=(Person &p)
	{	
		//判断有无堆区内存
		if (m_A != NULL)
		{
			delete m_A;
			m_A = NULL;
		}
		//提供深拷贝,解决浅拷贝的问题
		m_A = new int(*p.m_A);
		return *this;
	}

3. 菱形继承

对虚基类的继承,产生vbptr虚基表指针,指向虚基表,虚基表记录的是当前位置距离下面虚基类的偏移量。也就是全局只有一个父类的变量,值为最后一次修改的量。

//菱形继承问题
class Animal
{
public:
	int m_Age;

};
//继承前加virtual关键字之后,变为虚继承;
//此时公共的父类Animal成为虚基类
class Sheep : virtual public Animal {};
class Tuo:virtual public Animal{};
class SheepTuo :public Sheep, public Tuo {};

void test01() 
{
	SheepTuo st;
	st.Sheep::m_Age = 100;
	st.Tuo::m_Age = 10;
	cout << st.m_Age << endl;
}

4. 动态多态

多态性满足条件:

  1. 有继承关系
  2. 子类重写父类中的虚函数

多态性的使用:

父类指针或引用指向子类对象

我们希望传入声明对象,那么就调用什么对象的函数:

  1. 如果函数地址在编译阶段就确定,那么是静态联编
  2. 如果函数地址在运行阶段才能确定,那么就是动态联编

产生vfptr(虚函数指针)指向vftab`(虚函数表);
父类的指针或者引用指向子类对象时候,发生多态性;
子类的虚函数表 内部 会替换子类的虚函数地址;

#include<iostream>
using namespace std;

class Animal
{
public:
	//函数前面加上virtual,变成虚函数,那么编译器在编译的时候就不能确定该函数调用了
	virtual void speak()
	{
		cout << "动物在说话" << endl;
	}
};
class Cat:public Animal
{
public:
	void speak()
	{
		cout << "小猫在说话" << endl;
	}
};
class Dog :public Animal
{
	void speak()
	{
		cout << "小狗在说话" << endl;
	}
};

void DoSpeak(Animal &animal)
{
	animal.speak();
}
void test01() 
{
	Cat cat;
	Dog dog;
	DoSpeak(cat);
	DoSpeak(dog);
}

int main() {
	test01();
	system("pause");
	return 0;

};

5. 纯虚函数和抽象类

class Base
{
public:
	//纯虚函数
	//只要有一个纯虚函数,这个类为抽象类
	//抽象类特点:
	//1. 无法实例化对象
	//2. 抽象类的子类 必须要重写父类的纯虚函数 ,不然也是抽象类
	virtual void func() = 0;
};
class Son:public Base
{
	 void func() {};

};

void test01() 
{
	Base *b=new Son;
	b->func();

}

6. 虚析构和纯虚析构

如果子类中有属性开辟到堆区,解决父类指针释放子类对象不干净的问题

#include<iostream>
using namespace std;
#include<string>
//纯虚函数和抽象类
class Animal
{
public:

	Animal()
	{
		cout << "Animal构造函数" << endl;
	}
	virtual void speak()=0;
	//利用虚析构解决父类指针释放子类对象不干净的问题
	//virtual ~Animal()
	//{
	//	cout << "Animal析构函数" << endl;
	//}
	virtual  ~Animal() = 0;
};
Animal::~Animal()
{
	cout << "Animal纯析构函数" << endl;
}


class Cat :public Animal
{
public:
	Cat(string name)
	{
		cout << "猫构造函数" << endl;
		m_Name =new string(name);
	}
	void speak()
	{
		cout <<*m_Name<< "小猫在说话" << endl;
	}
	~Cat()
	{
		if (m_Name != NULL)
		{	
			cout << "猫析构函数被调用" << endl;
			delete m_Name;
			m_Name = NULL;
		}
	}
	
	string *m_Name;

};

void test01() 
{
	Animal *animal = new Cat("Tom");
	animal->speak();
	//父类指针在析构时候,不调用子类的析构,导致内存泄漏
	delete animal;

}

int main() {
	test01();
	system("pause");
	return 0;

};

7. 函数模板

调用规则 :
  1. 如果函数模板和普通函数都可以实现,优先调用普通函数;
  2. 可以通过空模板参数列表来强制调用函数模板;
  3. 函数模板可以发生重载;
  4. 如果函数模板可以产生更好的类型匹配,有限调用函数模板
隐式类型转换:
  1. 普通函数可以实现;
  2. 函数模板:自动类型推导 不能;显示指定类型 可以发生隐式类型转换;
对于特点的数据类型,用具体化方式特殊实现

利用具体化Person的版本实现代码,具体化优先调用???

#include<iostream>
using namespace std;
#include<string>
//函数模板 用于选择排序

template <typename T> //声明模板
void sawp(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}
template<typename T>
void mySort(T arr[],int len)
{
	for (int i = 0; i < len; i++)
	{
		int max = i;//认定最大值的下标
		for (int j = i + 1; j < len; j++)
		{
			if (arr[max] < arr[j])
			{
				max = j;
			}
		}
		if (max != i)
		{
			//交换max和i元素
			swap(arr[max], arr[i]);
		}
	}
	
}
template<typename T>
void PrintArr(T arr[],int len)
{
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << ""; 
	}
		cout<< endl;
}
void test01()
{
	//char charArr[] = "fabcv";
	int charArr[] = { 1,2,3,4 };

	int len = sizeof(charArr) / sizeof(int);
	mySort(charArr, len);
	PrintArr(charArr, len);

}
int main() {
	test01();
	system("pause");
	return 0;

};

8. 类模板

  1. 类模板没有自动类型推导的使用方式
  2. 类模板在模板的参数列表中可以有默认参数
  3. 类模板中成员函数不是一开始创建,是在调用的时候才去创建
#include<iostream>
using namespace std;
#include<string>
//类模板

template <class NameType ,class AgeType> //声明模板
	//template <class NameType ,class AgeType=int> //指定默认参数
class Person
{
public:
	Person(NameType name, AgeType age) :m_Name(name), m_Age(age) {};
	void showPerson()
	{
		cout << m_Name << " " << m_Age << endl;
	}

	NameType m_Name;
	AgeType  m_Age;
};

void test01()
{
	Person<string, int> p1("站站" ,999);//类模板没有自动类型推导的使用方式
	p1.showPerson();

}
int main() {
	test01();
	system("pause");
	return 0;
};

8.2. 类模板对象做函数参数

  1. 指定传入类型 (建议使用)
  2. 参数模板化
  3. 整个类模板化
#include<iostream>
using namespace std;
#include<string>
//类模板

template <class NameType ,class AgeType> //声明模板
class Person
{
public:
	Person(NameType name, AgeType age) :m_Name(name), m_Age(age) {};
	void showPerson()
	{
		cout << m_Name << " " << m_Age << endl;
	}

	NameType m_Name;
	AgeType  m_Age;
};

//1.指定传入类型
void PrintPerson1(Person<string, int> &p)
{
	p.showPerson();
}

//2.参数模板化
template <class T1, class T2> 
void PrintPerson2(Person<T1, T2>& p)
{
	p.showPerson();
	cout << "T1的类型是:  " << typeid(T1).name() << endl;
	cout << "T2的类型是:  " << typeid(T2).name << endl;

}
//3.整个类模板化
template <class T> 
void PrintPerson3(T& p)
{
	p.showPerson();
	cout << "T的类型是:  " << typeid(T).name() << endl;

}

void test01()
{
	Person<string , int> p1("站站" ,999);
	//PrintPerson1(p1);
	//PrintPerson2(p1);
	PrintPerson3(p1);
}
int main() {
	test01();
	system("pause");
	return 0;
};

8.3. 类模板继承

  1. 子类继承的父类是一个类模板时,子类在声明的时候,需要指定出父类中T的类型.如果不指定,编译器无法给子类分配内存。
class Son1:pubulic Base<int>
{};
  1. 如果想灵活支出父类中T的类型,子类也需变为类模板
template<class T1,class T2>
class Son2:pubulic Base<T2>
{
	T1 obj;
};

8.4. 类模板成员函数类外实现

需要加上模板参数列表

//构造函数
template<class T1,class T2>
Person<T1,T2>::Person(T1 name,T2 age){}
//成员函数
template<class T1,class T2>
void Person<T1,T2>::showPerson(){}

8.5. 类模板分文件编写

问题:模板总成员函数创建时机实在调用阶段,导致分文件编写时链接不到

解决:将声明和实现写到同一个文件中,并更改后缀为.hpp,hpp是约定的名称,并不是强制

//person.hpp文件中书写
#pragma once
#include<iostream>
using namespace std;
#include<string>

template <class NameType, class AgeType> //声明模板
class Person
{
public:
	Person(NameType name, AgeType age);
	void showPerson();

	NameType m_Name;
	AgeType  m_Age;
};

template<class NameType, class AgeType>
Person<NameType, AgeType>::Person(NameType name, AgeType age)
{
	this->m_Name = name;
	this->m_Age = age;
}

template<class NameType, class AgeType>
void Person<NameType, AgeType>::showPerson()
{
	cout << this->m_Name << " " << this->m_Age << endl;
}

8.6 类模板友元函数的实现

  1. 全局函数配合友元 类内定义
  2. 类外定义:提前声明类模板;声明函数模板;类内用类外实现方式的友元声明(加空模板参数列表);类外定义;
#include<iostream>
using namespace std;
#include<string>

//2.全局函数配合友元 类外实现 - >1.先做函数模板声明,下方再做函数模板定义,再做友元
template<class T1, class T2>
class Person;

template<class T1, class T2>
 void printPerson2 (Person<T1, T2> & p);


template<class T1, class T2>
class Person
{
	//1.全局函数配合友元  类内实现
	friend void printPerson(Person<T1, T2>& p)
	{
		cout << "姓名:" << p.m_Name << "\n年龄  " << p.m_Age << endl;
	}

	//2.全局函数配合友元  类外实现 ->2.内类友元声明,加空模板的参数列表
	friend void printPerson2<>(Person<T1, T2>& p);

public:
	Person(T1 name, T2 age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}

private:
	T1 m_Name;
	T2 m_Age;
};
//2.全局函数配合友元  类外实现 3.类外定义
template<class T1, class T2>
void printPerson2(Person<T1, T2>& p)
{
	cout << "姓名:" << p.m_Name << "\n年龄  " << p.m_Age << endl;
}


void test01()
{
	Person<string , int> p1("站站" ,999);
	printPerson(p1);
	printPerson2(p1);

}
int main() {
	test01();
	system("pause");
	return 0;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值