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. 动态多态
多态性满足条件:
- 有继承关系
- 子类重写父类中的虚函数
多态性的使用:
父类指针或引用指向子类对象
我们希望传入声明对象,那么就调用什么对象的函数:
- 如果函数地址在编译阶段就确定,那么是静态联编
- 如果函数地址在运行阶段才能确定,那么就是动态联编
产生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. 函数模板
调用规则 :
- 如果函数模板和普通函数都可以实现,优先调用普通函数;
- 可以通过空模板参数列表来强制调用函数模板;
- 函数模板可以发生重载;
- 如果函数模板可以产生更好的类型匹配,有限调用函数模板
隐式类型转换:
- 普通函数可以实现;
- 函数模板:自动类型推导 不能;显示指定类型 可以发生隐式类型转换;
对于特点的数据类型,用具体化方式特殊实现
利用具体化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. 类模板
- 类模板没有自动类型推导的使用方式
- 类模板在模板的参数列表中可以有默认参数
- 类模板中成员函数不是一开始创建,是在调用的时候才去创建
#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. 类模板对象做函数参数
- 指定传入类型 (建议使用)
- 参数模板化
- 整个类模板化
#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. 类模板继承
- 子类继承的父类是一个类模板时,子类在声明的时候,需要指定出父类中T的类型.如果不指定,编译器无法给子类分配内存。
class Son1:pubulic Base<int>
{};
- 如果想灵活支出父类中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 类模板友元函数的实现
- 全局函数配合友元 类内定义
- 类外定义:提前声明类模板;声明函数模板;类内用类外实现方式的友元声明(加空模板参数列表);类外定义;
#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;
};