【C++】动态内存管理和泛型编程

2023-11-18


需要云服务器等云产品来学习Linux的同学可以移步/-->腾讯云<--/-->阿里云<--/-->华为云<--/官网,轻量型云服务器低至112元/年,新用户首次下单享超低折扣。 


目录

一、C/C++内存区域划分

二、常见变量存储区域

三、new和delete

1、new和delete的使用方式

2、new、delete和malloc、free的区别

3、new的原理

4、delete的原理

5、new T[N]原理

6、delete[]原理

四、定位new

1、定位new的概念

2、定位new的使用格式

3、定位new的使用场景

五、泛型编程

六、函数模板

1、函数模板的使用

2、不同类型形参传参时的处理

2.1传参时强转(对应形参需要const修饰)

2.2显式实例化(传参时隐式类型转换,对应形参需要const修饰)

2.3使用多个模板

3、模板和实例可以同时存在,编译器会优先调用实例 

七、类模板

1、对象定义时需要显式实例化

2、为什么stl被称为模板

3、类模板不支持声明和定义分离

八、非类型模板参数

九、模板的特化

1、函数模板特化

2、类模板的特化(应用场景:对仿函数的特化)

2.1全特化

2.2半特化(偏特化)

十、模板的优缺点

1、优点

2、缺点


一、C/C++内存区域划分

1. 又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的。

2. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。

3. 用于程序运行时动态内存分配,堆是可以上增长的。

4. 数据段--存储全局数据和静态数据。

5. 代码段--可执行的代码/只读常量。

二、常见变量存储区域

int globalVar = 1;//全局变量中在静态区
static int staticGlobalVar = 1;//静态区
void Test()
{
	static int staticVar = 1;//静态区
	int localVar = 1;//栈区
	int num1[10] = { 1, 2, 3, 4 };//栈区
	char char2[] = "abcd";//栈区,*char2在栈区
	const char* pChar3 = "abcd";//指针在栈区,*pchar3在常量区
	int* ptr1 = (int*)malloc(sizeof(int) * 4);//指针在栈区,*ptr1在堆区
	int* ptr2 = (int*)calloc(4, sizeof(int));///栈区
	int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);//栈区
	free(ptr1);
	free(ptr3);
}

三、new和delete

1、new和delete的使用方式

int main()
{
	int* p1 = new int;//在堆区申请一个int大小的空间,不会初始化
	int* p2 = new int(0);//申请并初始化为0
	delete p1;
	delete p2;

	int* p3 = new int[10];//在堆区申请一块10个int大小的空间,未初始化
	int* p4 = new int[10]{ 1,2,3,4 };//初始化为{1,2,3,4,0,0,0,0,0,0}
	delete[] p3;
	delete[] p4;
	return 0;
}

注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new T[]和delete[],一定要匹配起来使用。

vs中,类中显式写了析构函数,编译器认为这个类有资源,会在申请的空间之前再申请一小块空间,用于存储对象个数等信息,当new T[]和delete不匹配时,会少释放记录位,造成内存泄漏并报错。当类中不显示写析构函数时,将不会有记录位,不会造成内存泄漏和报错。当然每个编译器底层实现原理不同,中断代码在不同的平台会出现不同的后果,总之匹配使用就对了。 

2、new、delete和malloc、free的区别

1、对于内置类型,没有区别。

2、new和delete是C++的关键字/操作符,而malloc和free是C语言的库函数。

3、对于自定义类型,相比于malloc和free,new和delete会额外调用类中的构造函数和析构函数。

4、malloc的返回值是void*,使用时需要强转,new后边跟的是空间的类型,所以new不需要强转。

5、malloc失败返回空指针,需要判空;new失败抛异常,需要捕获异常。

3、new的原理

new等于operator new()+构造函数。operator new()不是new运算符的重载,因为参数没有自定义类型。它是一个库里的全局函数。

void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc) 
{
// try to allocate size bytes
    void *p;
    while ((p = malloc(size)) == 0)
         if (_callnewh(size) == 0)
         {
             // report no memory
             // 如果申请内存失败了,这里会抛出bad_alloc 类型异常
             static const std::bad_alloc nomem;
             _RAISE(nomem);
         }
    return (p);
}

从底层代码可以看出operator new()是对malloc的封装,如果malloc失败,将会抛出异常。

4、delete的原理

delete等于operator delete()+析构函数

//operator delete: 该函数最终是通过free来释放空间的
void operator delete(void *pUserData) {
     _CrtMemBlockHeader * pHead;
     RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
     if (pUserData == NULL)
         return;
     _mlock(_HEAP_LOCK);  /* block other threads */
     __TRY
         /* get a pointer to memory block header */
         pHead = pHdr(pUserData);
          /* verify block type */
         _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
         _free_dbg( pUserData, pHead->nBlockUse );//调用free()
     __FINALLY
         _munlock(_HEAP_LOCK);  /* release other threads */
     __END_TRY_FINALLY
     return; }
//free的实现
#define   free(p)               _free_dbg(p, _NORMAL_BLOCK)

从底层代码可以看出operator delete()调用了free。

所以针对内置类型或无资源的类对象delete时,使用delete和free效果相同。但对于有资源需要释放的对象时,直接使用free虽然释放了对象的空间,但对象内部的资源还未被清理,导致内存泄漏!这种情况必须使用delete。

5、new T[N]原理

1、new T[N]调用operator new[]

2、operator new[]调用operator new完成N个对象空间的开辟。

3、调用N次构造函数完成N个对象的初始化。

6、delete[]原理

1、调用N次析构函数完成N个对象资源的清理工作。

2、调用operator delete[]

3、operator delete[]调用operator delete完成整段空间的释放。

四、定位new

1、定位new的概念

对于一个类,我们可以显式的去调用类的析构函数,但是不能显式调用构造函数,那么使用定位new,就可以显式调用类的构造函数,对一块空间重新初始化。

2、定位new的使用格式

new (指针)类名或者new (指针) type(初始化列表)

int main()
{
	Date d1;
	new(&d1)Date;//new (指针)类名
	Date* p = new Date[4]{ {2022,10,15},{2023,11,8} };
	new(p)Date[4];//new (指针) type(初始化列表)
	delete[] p;
	return 0;
}

上述代码一共调用了10次构造函数,经过定位new的处理,d1和p所代表的空间已经被重新初始化了。

3、定位new的使用场景

一般不会像上边代码一样,对一块已有对象数据的空间重新初始化。定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,对于自定义类型的对象,可以使用定位new对这些没有被初始化的内存显式调用类的构造函数初始化。

五、泛型编程

泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。

模板分为函数模板和类模板

六、函数模板

1、函数模板的使用

template<typename T>
void Swap(T& a, T& b)
{
	T tmp = a;
	a = b;
	b = tmp;
}
int main()
{
	int a = 10, b = 5;
	double m = 2.3, n = 4.9;
	Swap(a, b);
	Swap(m, n);
	return 0;
}

两个Swap调用的不是模板,而是模板生成的实例化函数,像上述代码中,模板会生成int和double类型的两种实例化函数。

2、不同类型形参传参时的处理

2.1传参时强转(对应形参需要const修饰)

template<typename T>
T Add(const T& a,const T& b)//const接收常性实参
{
	return a + b;
}
int main()
{
	int a = 10, b = 5;
	double m = 2.3, n = 4.9;
	Add(a, (int)m);//强转,临时变量传参,具有常性
	return 0;
}

使用强制类型转换在推演的时候将形参转换成同一类型。

2.2显式实例化(传参时隐式类型转换,对应形参需要const修饰)

template<typename T>
T Add(const T& a, const T& b)//需要使用const接收
{
	return a + b;
}
int main()
{
	int a = 10, b = 5;
	double m = 2.3, n = 4.9;
	Add<int>(a, m);//显式实例化,m发生隐式类型转换
	return 0;
}

显式实例化编译器不再去推演T的类型,而是直接使用尖括号内的类型实例化对应函数。

2.3使用多个模板

template<typename T1,class T2>//可以写typename也可以写class
T1 Add(const T1& a, const T2& b)
{
	return a + b;
}
int main()
{
	int a = 10, b = 5;
	double m = 2.3, n = 4.9;
	Add(a, m);//Add<int,double>(a,m);多个模板的手动推演
	return 0;
}

3、模板和实例可以同时存在,编译器会优先调用实例 

template<typename T>//可以写typename也可以写class
T Add(const T& a, const T& b)
{
	return a + b;
}
int Add(const int& a, const int& b)
{
	return a + b;
}
int main()
{
	int a = 10, b = 5;
	double m = 2.3, n = 4.9;
	Add(a, m);//调用已有实例
	Add<int>(a, m);//调用模板生成的实例
	return 0;
}

1、模板和普通函数的函数名修饰规则是不一样的。

2、模板和实例可以同时存在,编译器会优先调用实例。如果想使用模板生成的实例,必须使用尖括号指定类型。

3、如果模板可以生成更加匹配的版本,编译器将会生成这个匹配版本而不是使用那个已有但不太匹配的实例。

七、类模板

1、对象定义时需要显式实例化

int main()
{
	Stack<double> st1; // double
	st1.Push(1.1);
	Stack<int> st2; // int
	st2.Push(1);
	return 0;
}

函数模板可以通过传参确定T的类型,但是类模板编译器无法推演,必须要在对象定义时显式实例化类型。

模板参数不同,他们就是不同的类型。st1和st2属于不同的类定义出的两个对象。所以不能有st1=st2,因为他们不是同一个类,除非针对这种赋值,自己写一个赋值重载。

2、为什么stl被称为模板

类模板和函数模板不同,需要在实例化的时候在类名后加上<类型>。
类模板不是真正的类,而实例化出来的才是真正的类。

// Vector是类模板,Vector<int>才是类型
Vector<int> s1;
Vector<double> s2;

3、类模板不支持声明和定义分离

template<typename T>
class Stack
{
public:
	Stack(int capacity = 4);
	~Stack();
	void Push(const T& x);

private:
	T* _a;
	int _top;
	int _capacity;
};
//成员函数的定义
template<class T>
Stack<T>::Stack(int capacity = 4)
{
	cout << "Stack(int capacity = )" << capacity << endl;
	_a = (T*)malloc(sizeof(T)*capacity);
	if (_a == nullptr)
	{
		perror("malloc fail");
		exit(-1);
	}
	_top = 0;
	_capacity = capacity;
}
template<class T>
Stack<T>::~Stack()
{
	cout << "~Stack()" << endl;
	free(_a);
	_a = nullptr;
	_top = _capacity = 0;
}
template<class T>
void Stack<T>::Push(const T& x)
{
	// ....
	_a[_top++] = x;
}

类模板和其成员函数不支持声明和定义分离。因为类模板和函数模板都不是真正的定义,真正的定义是在模板实体化的时候由编译器完成。如果将模板的定义部分和实现部分分离开来,编译器真正要去完成模板实体化的时候就会因为找不到相应的代码而发生链接错误。

解决方法:

1、模板定义的位置显式实例化

template class stack<int>;//需要在定义的地方手动指明实例化的类型

2、声明和定义不分离,全部放到一个.hpp或.h的文件下。

八、非类型模板参数

想要生成两个Array对象,C语言只能使用宏定义的方式确定N的大小,但这样生成的对象的N的大小都是一样的。

C++可以在模板中使用非类型模板参数,生成的Array对象中的N可以任意指定。

//#define N 10//C语言玩法
template <class T,size_t N=10>//T是类型模板参数,N是非类型模板参数
class Array
{
private:
    T _arr[N];
};
int main()
{
    Array<int, 20> a1;
    Array<double, 100> a2;
    return 0;
}

不过非类型模板参数只支持整型常量,浮点型、变量、类对象等都不行。

九、模板的特化

模板特化:在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。

1、函数模板特化

// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{
	return left < right;
}

// 针对某些类型进行特殊处理 -- Date* --这是bool Less(T left, T right)函数模板的特化
//template<>
//bool Less<Date*>(Date* left, Date* right)
//{
//	return *left < *right;
//}

//进行函数重载
//bool Less(Date* left, Date* right)
//{
//	return *left < *right;
//}

int main()
{
	cout << Less(1, 2) << endl;   // 可以比较,结果正确

	Date d1(2022, 7, 7);
	Date d2(2022, 7, 8);
	cout << Less(d1, d2) << endl;  // 可以比较,结果正确

	Date* p1 = &d1;
	Date* p2 = &d2;
	cout << Less(p1, p2) << endl;  // 可以比较,结果错误
	return 0;
}

Less(p1, p2)并不是按照指针所指向的数据比较,而是按照地址进行比较。解决方法有两个:1、写一个less函数的重载版本。2、使用模板的特化。

2、类模板的特化(应用场景:对仿函数的特化)

2.1全特化

template<class T1, class T2>
class Data
{
public:
	Data() { cout << "Data<T1, T2>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};
// 全特化
template<>
class Data<double, double>
{
public:
	Data() { cout << "Data<double, double>" << endl; }
private:
	double _d1;
	double _d2;
};

2.2半特化(偏特化)

template<class T1, class T2>
class Data
{
public:
	Data() { cout << "Data<T1, T2>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};
// 半特化、偏特化
template<class T1>
class Data<T1, char>
{
public:
	Data() { cout << "Data<T1, char>" << endl; }
};
//半特化对参数类型进一步限制
template<class T1, class T2>
class Data<T1*, T2*>
{
public:
	Data() { cout << "Data<T1*, T2*>" << endl; }
};

template<class T1, class T2>
class Data<T1&, T2&>
{
public:
	Data() { cout << "Data<T1&, T2&>" << endl; }
};

编译器是个懒狗,哪个类型更加匹配,就用哪个模板或模板特化。

十、模板的优缺点

1、优点

模板增强了代码之间的复用性,增强代码的灵活性,更快迭代开发。

2、缺点

模板会导致代码膨胀,编译时间变长。

出现模板编译错误时,报错信息凌乱,不易定位错误。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【C++】动态内存管理和泛型编程 的相关文章

  • 为什么存在 async 关键字

    浏览 msdn 9 频道视频时 我发现以下未答复的评论 希望有人能解释一下 我不明白 async 关键字的意义 为什么不直接允许 任何时候方法返回任务时都会使用await关键字 就像迭代器一样 可以在任何返回 IEnumerable 的方法
  • 内联函数/方法

    声明 内联函数必须在调用之前定义 这个说法正确吗 EDIT 该问题最初是德语 内联功能穆森 弗 伊赫雷姆 奥夫鲁夫定义 sein 也许它对任何人都有帮助 是的 它是正确的 但只是部分正确 它可能正确地重新构建如下 内联函数必须在每个翻译单位
  • C# 正则表达式用于查找 中具有特定结尾的链接

    我需要一个正则表达式模式来查找字符串 带有 HTML 代码 中的链接 以获取文件结尾如 gif 或 png 的链接 示例字符串 a href site com folder picture png target blank picture
  • 如何使用MySqlCommand和prepare语句进行多行插入?(#C)

    Mysql 给出了如何使用准备语句和 NET 插入行的示例 http dev mysql com doc refman 5 5 en connector net programming prepared html http dev mysq
  • 获取尚未实例化的类的函数句柄

    我对 C 相当陌生 我想做的事情可能看起来很复杂 首先 我想获取一些函数的句柄以便稍后执行它们 我知道我可以通过以下方式实现这一目标 List
  • 将字符串转换为正确的 URI 格式?

    有没有简单的方法可以将电子邮件地址字符串转换为正确的 URI 格式 Input http mywebsite com validate email 3DE4ED727750215D957F8A1E4B117C38E7250C33 email
  • 无法解析远程名称 - webclient

    我面临这个错误 The remote name could not be resolved russgates85 001 site1 smarterasp net 当我请求使用 Web 客户端读取 html 内容时 出现错误 下面是我的代
  • TcpClient 在异步读取期间断开连接

    我有几个关于完成 tcp 连接的问题 客户端使用 Tcp 连接到我的服务器 在接受客户端后listener BeginAcceptTcpClient ConnectionEstabilishedCallback null 我开始阅读netw
  • 为什么 clang 使用 -O0 生成低效的 asm(对于这个简单的浮点和)?

    我正在 llvm clang Apple LLVM 版本 8 0 0 clang 800 0 42 1 上反汇编此代码 int main float a 0 151234 float b 0 2 float c a b printf f c
  • OpenCV 2.4.3 中的阴影去除

    我正在使用 OpenCV 2 4 3 最新版本 使用内置的视频流检测前景GMG http docs opencv org modules gpu doc video html highlight gmg gpu 3a 3aGMG GPU算法
  • 使用 WF 的多线程应用程序的错误处理模式?

    我正在写一个又长又详细的问题 但只是放弃了它 转而选择一个更简单的问题 但我在这里找不到答案 应用程序简要说明 我有一个 WPF 应用程序 它生成多个线程 每个线程执行自己的 WF 处理线程和 WF 中的错误 允许用户从 GUI 端进行交互
  • 分配器感知容器和propagate_on_container_swap

    The std allocator traits模板定义了一些常量 例如propagate on container copy move assign让其他容器知道它们是否应该在复制或移动操作期间复制第二个容器的分配器 我们还有propag
  • C# 中的常量和只读? [复制]

    这个问题在这里已经有答案了 可能的重复 const 和 readonly 之间有什么区别 https stackoverflow com questions 55984 what is the difference between cons
  • tabcontrol selectedindex 更改事件未被触发 C#

    嘿伙计们 我有一个很小的问题 请参阅下面的代码 this is main load private void Form1 Load object sender EventArgs e tabAddRemoveOperator Selecte
  • 从 R 到 C 处理列表并访问它

    我想使用从 R 获得的 C 列表 我意识到这个问题与此非常相似 使用 call 在 R 和 C 之间传递数据帧 https stackoverflow com questions 6658168 passing a data frame f
  • asp.net网格分页的SQL查询

    我在用iBatis and SQLServer 使用偏移量和限制进行分页查询的最佳方法是什么 也许我添加该列ROW NUMBER OVER ORDER BY Id AS RowNum 但这只会阻止简单查询的数据访问 在某些情况下 我使用选择
  • 为什么要在 C++ 中使用 typedef?

    可以说我有 set
  • DataTable:通过 LINQ 或 LAMBDA 进行动态 Group By 表达式

    我有一个数据表 我想在其中对未指定数量的字段进行分组 发生这种情况的原因是用户可以选择他想要分组的字段 所以 实际上 我将选择推入列表中 在这个选择上 我必须对我的数据表进行分组 想象一下这段代码 VB 或 C 都一样 public voi
  • 使用 iTextSharp 5.3.3 和 USB 令牌签署 PDF

    我是 iTextSharp 和 StackOverFlow 的新手 我正在尝试使用外部 USB 令牌在 C 中签署 PDF 我尝试使用从互联网上挖掘的以下代码 Org BouncyCastle X509 X509CertificatePar
  • 服务器响应 PASV 命令返回的地址与建立 FTP 连接的地址不同

    System Net WebException 服务器响应 PASV 命令返回的地址与建立 FTP 连接的地址不同 在 System Net FtpWebRequest CheckError 在 System Net FtpWebReque

随机推荐

  • 想搞清是服务器否存在内存泄漏或jvm其他方面的问题

    解决问题 想搞清是服务器否存在内存泄漏或jvm其他方面的问题 heap dump heap dump文件是一个二进制文件 它保存了某一时刻JVM堆中对象使用情况 HeapDump文件是指定时刻的Java堆栈的快照 是一种镜像文件 Heap
  • 深度学习总结(一)

    深度学习总结 一 1 经典优化算法 1 一阶迭代法 又称梯度下降法 2 二阶迭代法 牛顿法 一般在神经网络里面 L 函数就是代价函数 2 不同梯度下降法 1 经典梯度下降法 2 随机梯度下降法 随机梯度下降法可以解决经典梯度下降法数据量大
  • 代码随想录算法训练营第一天

    704 二分查找 题目链接 力扣 二分法写代码时一般是写左闭右闭和左闭右开两种类型 左闭右闭 left right 左闭右开 left right 指右边不包含right这个值 int right size 两大问题 while left
  • 全新的刷脸支付开辟一条全新发展之路

    数字化和刷脸支付的强强联合给众多商家带去希望和惊喜 崭新的2021年 这个惊喜仍然在继续 数字化经营刷脸支付 如何为创业者带去商机 2020年 是刷脸支付发展的黄金时期 它曾因为疫情跌落到谷底 却也因为疫情再次飞上云端 重拾自信 在行业巨头
  • form 校验多个表单

    有的时候 表单需要拆开多个 这时候就需要校验多个表单
  • Qt 学习之路:线程和 QObject

    前面两个章节我们从事件循环和线程类库两个角度阐述有关线程的问题 本章我们将深入线程间得交互 探讨线程和QObject之间的关系 在某种程度上 这才是多线程编程真正需要注意的问题 现在我们已经讨论过事件循环 我们说 每一个 Qt 应用程序至少
  • c# winform对数据库进行增删改查操作

    开发工具 sqlserver2012 visoual code 2017 打开sqlserver2012 创建一个表 表结构如下 然后打开VS2017 文件 新建 项目 Windows窗体应用 这里我就在工具箱拉取了三个button和一个显
  • VS Code+Anaconda(国内源)配置python

    一 Anaconda的介绍 为何使用 优点 二 Anaconda下载和安装 三 VS Code下载及安装 四 Anaconda更换国内源 原因及操作方法 操作方法 具体操作方法 记得看上边优秀博客 五 TensorFlow环境 配置原因及优
  • equals底层

    Equals底层实现 这篇文章有抄两个博主的东西 请不要介意 学习最重要 主要怕你们什么时候删帖看不到 谢谢 在基础类型中都重写了equals方法 但是Object中的equals的方法如果不重写就没有意义 因为源代码中equals直接用
  • Equals和HashMap的重写

    一 首先 老铁们应该先了解API中的HashCode和equals解释 1 如果两个对象相同 即用equals比较返回true 那么它们的hashCode值一定要相同 2 如果两个对象的hashCode相同 它们并不一定相同 即用equal
  • 光敏传感器简介

    光敏传感器 1 简介 光敏传感器是最常见的传感器之一 它的种类繁多 主要有 光电管 光电倍增管 光敏电阻 光敏三极管 太阳能电池 红外线传感器 紫外线传感器 光纤式光电传感器 色彩传感器 CCD和CMOS图像传感器等 光传感器是目前产量最多
  • Spring boot按日切分nohup.out日志文件的方法

    过大的日志文件维护起来存在诸多问题 所以最好是能够按日或按大小切分日志文件 下面给大家带来了Spring boot按日切分spring boot的nohup out日志文件的方法 方法如下 1 安装cronolog 2 执行以下命令启动应用
  • 完美解决umi+ProLayout 部分菜单动态的问题

    项目中用到这个框架 当然是很好用且方便的 但是实际使用的时候发现项目中限制了一些自定义内容 踩了几个坑 记录一下 动态菜单调用接口异步 页面上显示空白 解决方案 将方法放在getInitialState中查询 存在initialState里
  • 线性回归算法--拟合正弦函数

    目录 步骤 代码实现 本博客参考书籍 scikit learn机器学习 常用算法原理及编程实战 本博客源码地址 码云 步骤 生成200个在 2 2
  • Jeesite权限处理,权限分配,根据不同的用户展示不同的信息,按钮权限等

    jeesite关于权限这方面的记录或者文章很少 看官方文档又看不懂 自己的业务又需要进行权限处理 怎么办 当然问大佬了 我就记录下我的解决办法 给jeesite权限方面的文章做点贡献 我先说下我的业务逻辑 我需要实现不同公司的人登陆后台 只
  • 相似矩阵反推标签

    Background 有监督的多模态检索 supervised multi modal retrieval 中 常用 label 构造相似矩阵 S 样本集 X x i
  • vue中组件之间传递数组

    let InFo JSON stringify arr localStorage setItem array InFo 通过 JSON stringify 将数组解析成字符串 let Info JSON parse localStorage
  • gcc -c -o编译过程

    gcc编译 分步处理 一 预处理 二 编译 三 汇编 四 链接 一步到位 多模块编译 一次性编译 独立编译 C源文件到可执行文件共经历了4个过程 在使用GCC编译程序时 编译过程可以被细分为四个阶段 包括预处理 编译 汇编 链接 分步处理
  • 分析排序算法的时间复杂度和空间复杂度

    1 冒泡排序 时间复杂度 O n 2 空间复杂度 O 1 冒泡排序需要进行n 1趟冒泡 每一趟需要比较n i次 最坏情况下需要交换n 1次 故时间复杂度为O n 2 冒泡排序的空间复杂度是O 1 因为只需要使用一个临时变量即可 2 选择排序
  • 【C++】动态内存管理和泛型编程

    需要云服务器等云产品来学习Linux的同学可以移步 gt 腾讯云 lt gt 阿里云 lt gt 华为云 lt 官网 轻量型云服务器低至112元 年 新用户首次下单享超低折扣 目录 一 C C 内存区域划分 二 常见变量存储区域 三 new