云计算百科
云计算领域专业知识百科平台

C++内存管理 + 模板

在这里插入图片描述

༺ 个人主页 · 纪念229 ༻

🏠我的博客主页🏠

༒专栏目录:《数据结构》༒

༒专栏目录:《算法》༒

༒专栏目录:《C++》༒

༒专栏目录:《MySQL数据库》༒

༒专栏目录:《前端开发》༒

༒其它有趣的计算机知识༒

༺世上本没有路,走的人多了自然就有了༻


本篇文章讲述的是C++内存管理和模板的内容,希望对你有所帮助

本篇文章主要以代码的形式讲解


文章目录

  • 1.内存管理
  • 2.模板

1.内存管理

#include<iostream>
using namespace std;

//class A
//{
//public:
//A(int a = 0)
//:_a(a)
//{
//cout << "A(int a = 0)" << endl;
//}
//~A()
//{
//cout << "~A()" << endl;
//}
//private:
//int _a;
//};
//
//int main()
//{
//A* p5 = (A*)malloc(sizeof(A) * 10);
//free(p5);
//
//A* p6 = new A[10];
//delete[] p6;
//return 0;
//}

这里讲的是malloc与new申请空间和销毁的方式,之前文章解释过这里就不赘述了

//#define _DEBUG_BIT

//void func()
//{
//for (size_t i = 0; i < 100000; i++)
//{
////int* p1 = (int*)malloc(sizeof(int) * 1024 * 1024); //4M
////if (p1)
////{
////cout << i << "->" << p1 << endl;
////}
////else
////{
////break;
////}
//#ifdef _DEBUG_BIT
//if (i == 493)
//{
//int x = 0;
//}
//#endif
//int* p1 = new int[1024 * 1024];//4M
// //申请失败自动抛出异常
// cout << i << "->" << p1 << endl;
////这个#define XX
////
//// #ifdef XX //如果#define XX代码里的东西才能执行
//// 代码
//// #endif
//// 代码里写if 条件是 i == 397
////目的是在里面打一个断点当我们
////循环到i == 397 的时候再看
////用不着我们一个个点到第397次循环
////不想用这段代码将#define XX注释即可
////#ifdef
//// 代码 不再编译
//// #endif
////
//
//
//}
//}
//
//int main()
//{
////try/catch就是给抛异常代码的
////的接盘机制让程序死的体面
//
//
//
////try 和 catch
//// 结构
//// try
//// {
//// //可能出错的代码放这里
//// }
//// catch(异常类型 变量名)
//// {
//// //出错后处理的代码
//// }
////如果代码里的new失败抛异常
////catch就会接收异常
////并打出错在哪里
//// 异常有多种举列catch()
//// 这些都在小括号里
//// const bad_alloc& e 内存不足
//// const out_of_range& e 越界
//// const (std::)exception& e
//// 继承自std::expection的标准异常
//// 如std::runtime_error std::out_of_range
//// 这些异常类型都在std域内
////
//// const … 包含所有异常
//try
//{
//func();
// }
//catch (const exception& e)
//{
////这里为啥不用exception e
//// 原因:exception是类不加const &会触发拷贝构造
//cout << e.what() << endl;
////代码里的what()函数是exception类的一个成员函数
// //返回有错误信息的字符串
// //(错误信息字符串的内容由抛异常的地方决定)
////
//}
//return 0;
//}

先解释main函数里的 try { //代码(可能出现的代码) } catch(异常类型 变量名) { //出错后处理的代码 } 一般是new申请空间失败抛异常 代码里的new失败抛异常 catch就会接收异常 并打出异常在代码那一行

异常可以是任何类型(最常见的是new抛异常类型expection) 举列catch() 这些都在小括号里 返回意思 const bad_alloc& e 内存不足 const out_of_range& e 越界 const (std::)expection的标准异常 如std::runtime_error std::out_of_range(这些异常类型都在std域内)

…(表示所有异常)

try
//{
//func();
// }
//catch (const exception& e)
//{
////这里为啥不用exception e
//// 原因:exception是类不加const &会触发拷贝构造
//cout << e.what() << endl;
////代码里的what()函数是exception类的一个成员函数
// //返回有错误信息的字符串
// //(错误信息字符串的内容由抛异常的地方决定)
////
//}

这里为啥不用expection e 我们知道expection 这是一个类 我们在形参这样写会调用拷贝构造

这里what()是什么 首先我们知道e是expection类类型 what是该类中的成员函数(作用是打印抛异常的那一行代码)

接下来讲解预处理指令 //#define _DEBUG_BIT 这个的意思就是定义_DEBUG_BIT这个东西

下面两个预处理指令配套 //#ifdef _DEBUG_BIT //#endif

指令后面的东西就是个名字

//#ifdef _DEBUG_BIT
if (i == 493)
//{
//int x = 0;
//}
//#endif

我主要讲这个 如果定义了_DEBUG_BIT (#ifdef _DEBUG_BIT ) 里面的函数就可以使用(把它注释了或者没定义里面的函数就不可以用)

这段代码这要用于调试我们给条件语句里打一个断点 等循环到这就进入条件语句代码调式停止

如果不用这个代码我们想看第493次循环我们要点多少时间

浪费时间


//double Divide(int a, int b)
//{
////当b == 0时抛出异常
//if (b == 0)
//{
//string s("Divide by zero condition!");
//throw s;
//}
//else
//{
//return ((double)a / (double)b);
//}
//cout << __FUNCTION__ << ":" << __LINE__ << "行执行"
////这两个是宏定义标识符
////__FUNCTION__ 展开成当前行号
////__LINE__展开成当前函数名
////之前写的FUNCTION LINE
// //没在左右两边各加两个下划线
////由于没有""不会当成字符串
////不加""的字符串会当成名字去全局里找找不到就报未定义
//
//
//<< endl;
//}
//
//void main()
//{
//int len, time;
//cin >> len >> time;
//try
//{
//cout << Divide(len, time) << endl;
//}
//catch (const string& errmsg)
//{
//cout << errmsg << endl;
//}

这里的catch里放的string异常 代码里也是 throw s

一般用new抛异常就会自动throw 其它类型抛异常一般就是要手动throw抛异常

double Divide(int a, int b)
//{
////当b == 0时抛出异常
//if (b == 0)
//{
//string s("Divide by zero condition!");
//throw s;
//}
//else
//{
//return ((double)a / (double)b);
//}
//cout << __FUNCTION__ << ":" << __LINE__ << "行执行"
////这两个是宏定义标识符
////__FUNCTION__ 展开成当前行号
////__LINE__展开成当前函数名
////之前写的FUNCTION LINE
// //没在左右两边各加两个下划线
////由于没有""不会当成字符串
////不加""的字符串会当成名字去全局里找找不到就报未定义
//
//
//<< endl;
//}

这里就是防止分母为0就弄一个string类型的 s thow 就是抛异常catch就接收并打印异常原因

cout << __FUNCTION__ << ":" << __LINE__ << "行执行"

这两个是宏定义标识符

这两个字符串左右两边都必须各有两个下划线

否则就是名字(会从文件里找函数名、类名等找不到就报未定义)

用" "就打印的是字符串

作用 FUNCTION 展开当前行号

LINE 展开当前函数名


//typedef int STDataType;
//class Stack
//{
//public:
//Stack(int n = 4)
//{
//cout << "Stack(int n = 4)" << endl;
//
//_a = (STDataType*)malloc(sizeof(STDataType) * n);
//if (_a == nullptr)
//{
//perror("malloc申请空间失败");
////perror头文件#inlcude<cstdio>
//return;
//}
//_capacity = n;
//_top = 0;
//}
//~Stack()
//{
//cout << "~Stack()" << endl;
//free(_a);
//_a = nullptr;
//_capacity = _top = 0;
//}
//private:
//STDataType* _a;
//size_t _capacity;
//size_t _top;
//};
//
//
//
//int main()
//{
////Stack* p1 = new Stack[10];
////delete p1;
//
//int* p2 = new int(1);
////free(p2);
//delete p2;
//return 0;
//}

这里就是通过new申请空间给空间的元素初始化 然后销毁空间

new比malloc多了一个自动调用构造函数

delete 比free多了一个析构函数

这里的delete p1;因该改为delete[] p1因为这样只会调用一次析构函数

这样写在VS会报错(VS较严格)放在题目不会报错

//class A
//{
//public:
//A(int a = 0)
//:_a(a)
//{
//cout << "A(int a = 0)" << endl;
//}
//
//~A()
//{
//cout << "~A()" << this << endl;
//}
//private:
//int _a;
//};
//int main()
//{
///*A* p1 = new A[10];
//delete[] p1;*/
//
//A* p1 = (A*)malloc(sizeof(A));
////malloc不能调用构造
//
////不能这样显示调用构造
////p1->A(1);
//
////要这样
//new (p1)A(1);
////这里的A(1)不是默认对象
// //这个就是在p1内存中调用构造函数
// //
// // new A(1)
// // 这个是申请一个空间在调用构造
// //
// // new (p1)A(1);
// // 这个是直接在p1地址上调用构造函数
// //
////
//
////可以析构
////p1->~A();
//
//free(p1);
//return 0;
//}

malloc的对象不能自动调用构造函数可以调用析构函数(要手动调用) p1->~A();

malloc的对象调用构造函数要调用 new (p1)A(1);

意思是调用构造函数传入实参1,这个对象在p1这个地址中构造,最后返回指针

new (p1)A(1); new A(1);

区别一个已有地址把构造对象放在内存

一个向空间申请之后调用构造函数


2.模板

C++模板分为 函数模板 类模板

作用:简化代码减少逻辑重复性代码

我们来看看函数重载与函数模板

//void Swap(int& left, int& right)
//{
//int temp = left;
//left = right;
//right = temp;
//}
//
//void Swap(double& left, double& right)
//{
//double temp = left;
//left = right;
//right = temp;
//}
//
//void Swap(char& left, char& right)
//{
//char temp = left;
//left = right;
//right = temp;
//}

//函数模板
//泛型编程
//针对任意类型的对象交换
//
//函数模板内容与普通函数相同时
// 函数模板一般写在头文件
//相同时优先用普通函数

//结构
//template<typename/class 类型名>
// 一般用typename
// void 函数名(类型名 对象名)
// {
// 内容
//
// }

//template<typename T>
////template不要头文件C++自带
//void Swap(T& left, T& right)
//{
//T temp = left;
//left = right;
//right = temp;
//}
//
//int main()
//{
//int i = 0, j = 1;
//double x = 1.1, y = 2.2;
//Swap(i, j);
//Swap(x, y);
//return 0;
//}

函数重载就不赘述了,就是写了交换函数

函数模板结构

template<typename/class 类型名>

templateC++自带无头文件

typename/class都可以用

以交换函数为例

//template<typename T>
////template不要头文件C++自带
//void Swap(T& left, T& right)
//{
//T temp = left;
//left = right;
//right = temp;
//}

可以传任何内置类型

进行交换

//template<class T>
//T Add(const T& left, const T& right)
//{
//return left + right;
//}
//
//template<class T1, class T2>
//void func(const T1& left, const T2& right)
//{ }
//
//int main()
//{
//int a1 = 10, a2 = 20;
//double d1 = 10.1, d2 = 20.1;
////隐式实例化(实参类型推演模板参数)
//cout << Add(a1, a2) << endl;
//cout << Add(d1, d2) << endl;
//
//cout << Add(a1, (int)d2) << endl;
//cout << Add((double)a1, d2) << endl;
//
////显示实例化 (显示指定模板参数)
//cout << Add<int>(a1, d2) << endl;
//cout << Add<double>(a1, d2) << endl;
//cout << Add<int>(d1, d2) << endl;
//return 0;
//}

这里讲一下调用时如果实参类型不同或者想改变实参类型要用隐式实例化或显示实例化

////显示实例化 (显示指定模板参数)
//cout << Add<int>(a1, d2) << endl;
//cout << Add<double>(a1, d2) << endl;
//cout << Add<int>(d1, d2) << endl;

显示实例化 Add(a1, d2) 就是在函数名与小括号之间加个<> <>里面就是想要转换的类型

隐式实例化 Add(a1, (int)d2 就是直接将实参隐式类型转换


//专门处理int的加法函数
//int Add(int left, int right)
//{
//return left + right;
//
//}
//
//
////通用加法函数
//template<class T>
////Add函数返回值是int 不是void
//int Add(const T& left, const T& right)
//{
//return left + right + 10;
//}
//
//
//int main()
//{
//int a1 = 10, a2 = 20;
//double d1 = 10.1, d2 = 20.1;
////隐式实例化(实参类型推演模板参数)
//cout << Add(a1, a2) << endl;
////普通函数和函数模板会进行重载协议
////若是匹配度一样的话优先用普通函数
//cout << Add(d1, d2) << endl;
//cout << Add<int>(a1, a2);
////在匹配度相同的情况下用的(匹配度指的是比较返回值、函数名、形参)
////显示实例调用的是函数模板
//return 0;
//}

这里讲个函数的匹配度 返回类型 函数名 形参 相同就是完全匹配

当函数重载和函数模板中有完全匹配的函数就会触发重载协议 优先调用普通函数(函数重载),函数内容有差异无妨


类模板

//typedef double DateType;
//
//
//namespace bit
//{
//class stack
//{
//private:
//DateType* _a;
//int _top;
//int _capacity;
//};
//}
//
//int main()
//{
//bit::stack st1;//int
//bit::stack st2;//double
//
//return 0;
//}

namespace bit
{
//类模板(用法与类函数差不多)
template<class T>
class stack
{
public:
//这里要加public:
//否则调用不了构造函数
stack(int n = 4)
:_a(new T[n])
, _top(0)
, _capacity(n)
{
cout << "stack(int n = 4)" << endl;
}

void push(const T& x)
{
//..扩容
_a[_top] = x;
_top++;

}
~stack()
{
//new申请了空间最后要销毁
delete[] _a;
_a = nullptr;
_top = 0;
_capacity = 0;

cout << "~stack()" << endl;
}

private:
T* _a;
int _top;
int _capacity;
};

}

int main()
{
try
{
//显示实例化
bit::stack <int> st1;
//类模板和函数模板都可以用显示实例化和隐式实例化
bit::stack <double> st2;

//对象通过空间构造成功后
//对象就不用通过空间调用空间里的函数
st1.push(1);
st1.push(2);
st1.push(3);

st2.push(1.1);
st2.push(2.2);
st2.push(3.3);
}
catch (const exception& e)
{
cout << e.what() << endl;
}
return 0;
}

类模板和函数模板差不多这里就不赘述了

有了类模板我们想有什么类型的栈就用什么类型 等…


文章到此就告一段落,希望对你有所帮助,感谢观看!

赞(0)
未经允许不得转载:网硕互联帮助中心 » C++内存管理 + 模板
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!