【C++杂货铺】模板

这篇具有很好参考价值的文章主要介绍了【C++杂货铺】模板。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【C++杂货铺】模板,C++杂货铺,c++,java,开发语言,模板,热门

一、泛型编程

📖实现一个通用的交换函数

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;
}

想要实现一个通用的交换函数不难,借助函数重载就可以。函数重载小伙伴们还记得嘛👀,忘了的小伙伴可以走传送门回去复习一下。如上面代码所示,我们借助函数重载实现了三份Swap函数,分别用来交换两个整型变量、两个双精度浮点型变量、两个字符型变量。

小Tips:函数重载的通用性体现在函数调用的时候。当我们想交换两个变量的时候,不管变量是什么类型,都是直接使用Swap函数,编译器会根据用户传递的实参数据类型,自动去匹配调用对应的交换函数,在用户看来仿佛就只有一份Swap函数,实现了所有类型数据的交换。

📖函数重载的缺陷
通过上面的分析我们可以看出,函数重载的最大缺陷就是,当有一个新类型出现时,需要自己增加对应的重载交换函数,代码的复用率比较低。其次,因为所有的重载函数都是我们自己写的,过程繁琐并且难免会出现差错。

📖模板的引入
函数重载的主要问题就出在需要我们自己去写,那能否告诉编译器一个模子,让编译器代替我们,根据不同的类型利用该模子来生成代码呢?

【C++杂货铺】模板,C++杂货铺,c++,java,开发语言,模板,热门

如果在C++中,也能够存在这样一个模板,通过给这个模板涂上不同的颜色(类型),来获得不同颜色的五角星(生成具体的代码),那将会方便不少,我们的头发也能少掉一点。巧的是我们的先辈已经将树栽好,我们只需在此乘凉。

📖什么是泛型编程?
编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础,其中模板分为函数模板类模板

【C++杂货铺】模板,C++杂货铺,c++,java,开发语言,模板,热门

二、函数模板

📖概念
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。

📖格式

template <typename T1, typename T2,.......,typename Tn>
返回值类型 函数名(参数列表){函数体}

小Tipstypename是用来定义模板参数的关键字,也可以用class替换。T1T2…是模板参数,表示类型

//一个交换函数的函数模板
template<typename T>
void Swap( T& left, T& right)
{
	T temp = left;
	left = right;
	right = temp;
}

2.1 函数模板的原理

【C++杂货铺】模板,C++杂货铺,c++,java,开发语言,模板,热门

函数模板是一个蓝图,它本身并不是函数,是编译器用来产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的事情交给了编译器。

//一个交换函数模板
template<typename T>
void Swap(T& left, T& right)
{
	T temp = left;
	left = right;
	right = temp;
}

int main()
{
	int i1 = 10;
	int i2 = 20;
	Swap(i1, i2);

	double d1 = 1.1;
	double d2 = 2.2;
	Swap(d1, d2);

	char c1 = 'a';
	char c2 = 'b';
	Swap(c1, c2);
}

【C++杂货铺】模板,C++杂货铺,c++,java,开发语言,模板,热门
在编译器编译阶段,对于函数模板的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。例如:当用double类型的数据去调用Swap函数,编译器通过对实参类型的推演,将T确定为double类型,然后生成一份专门处理double类型的代码,对于int类型和char类型的数据也是这样处理的。所以在使用者看来,无论什么类型都是调用Swap函数,但本质上不同的类型会去调用不同的Swap函数。

【C++杂货铺】模板,C++杂货铺,c++,java,开发语言,模板,热门
小Tips:这里为了给大家展示函数模板的原理,专门写了一个交换函数Swap的模板,实际中当大家需要交换两个数据的时候不需要自己写,因为库中已经帮我们实现好了,可以用库中的swap(s小写)实现对两个同类型数据的交换。

2.2 函数模板的实例化

用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例化。

📖隐式实例化
隐式实例化就是让编译器根据实参,自动推演模板参数的实际类型。

template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}
int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.0, d2 = 20.0;
	Add(a1, a2);
	Add(d1, d2);
	//Add(a1, d1);//该语句编译不通过
	Add(a, (int)d);//强制类型转换,将两个参数设置成同类型
	return 0;
}

注意Add(a1, d1)会导致编译失败,因为在编译期间,当编译器看到该函数调用的时候,会去自动推演模板参数的类型,首先通过实参a1T推演为int,通过实参d1T推演为double,但是模板参数列表中只有一个T,编译器无法确定此处到底应该将T确定为int或者double类型而报错。此时这里有三种处理方法,第一种方法:在函数模板的参数列表中再增加一个模板参数;第二种方法:用户自己来强制类型转换,对一个参数进行强制类型转换,使得两个参数的类型相同;第三种方法:使用接下来介绍的显式实例化。

小Tips:模板参数也可以作为函数模板的返回值类型。

📖显式实例化
显式实例化是在函数调用阶段,在函数名后跟一个<>,在里面指定模板参数的实际类型。

template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}

int main(void)
{
	int a = 10;
	double b = 20.0;
	
	// 显式实例化
	Add<int>(a, b);
	return 0;
}

注意:如果传递的实参和实例化出的函数形参类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器会报错。

📖显式实例化的实际使用场景

template<typename T>//模板参数T
T* Alloc(int n)//函数模板的形参没有使用模板参数
{
	return new T[n];
}

int main()
{
	double* p = Alloc<double>(10);
	return 0;
}

如上面的代码所示,函数模板Alloc并没有模板参数T类型的形参,因此编译器就无法去根据用户传递的实参类型推导出模板参数T的具体类型。因此,当用户想要调用Alloc函数时,必须进行显式实例化。从这里也可以看出,编译器支持隐式实例化的前提是:模板函数使用了模板参数类型的形参。

小Tips:编译器不会根据函数的返回值去推导模板参数T的类型,就像上面的Alloc函数模板,虽然返回值的类型是模板参数T,但是不管用,编译器不会根据这里去推演T的实际类型,也推演不出来。

2.3 模板参数的匹配原则

  • 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数。
// 专门处理int的加法函数
int Add(int left, int right)
{
	return left + right;
}
// 通用加法函数
template<class T>
T Add(T left, T right)
{
	return left + right;
}
void Test()
{
	Add(1, 2); // 与非模板函数匹配,编译器不需要特化
	Add<int>(1, 2); // 调用编译器特化的Add版本
}
  • 对于非模板函数和同名的函数模板,如果其他条件都相同,在调用时会优先调用非模板函数,而不会从函数模板产生一个实例。如果函数模板可以产生一个具有更好匹配的函数,那么将选择函数模板。
// 专门处理int的加法函数
int Add(int left, int right)
{
	return left + right;
}
// 通用加法函数
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{
	return left + right;
}
void Test()
{
	Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化
	Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函数
}

三、类模板

📖类模板的定义格式

template<class T1, class T2, ..., class Tn>//模板参数列表
class 类模板名
{
 // 类内成员定义
}; 
// 动态顺序表
// 注意:Vector不是具体的类,是编译器根据被实例化的类型生成具体类的模具
template<class T>
class Vector
{ 
public :
	Vector(size_t capacity = 10)
	: _pData(new T[capacity])
	, _size(0)
	, _capacity(capacity)
	{}

// 使用析构函数演示:在类中声明,在类外定义。
~Vector();

void PushBack(const T& data);
void PopBack();
// ...

size_t Size() {return _size;}

private:
	T* _pData;
	size_t _size;
	size_t _capacity;
};
// 注意:类模板中函数放在类外进行定义时,需要加模板参数列表
template <class T>
Vector<T>::~Vector()
{
	if(_pData)
	delete[] _pData;
	_size = _capacity = 0;
}

注意Vector不是具体的类,是编译器根据被实例化的类型生成具体类的模具。其次,类模板中的成员函数放在类外面进行定义时需要加模板参数列表,因为此时单独的Vector已经不再表示类型了,编译器可能会根据Vector这个模板,同时实例化出多个类,此时Vector<T>表示一个具体的类型。建议类模板中的成员函数,声明和定义不要分离到两个文件中。

📖类模板的实例化
类模板的实例化与函数模板的实例化不同,类模板的实例化需要在类模板的名字后面跟<>,然后将实例化的类型放在<>中即可,类模板的名字不是真正的类,而实例化的结果才是真正的类

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

📖类模板的优势
类模板的优势和函数模板一样,把本来需要我们自己干的事情,交给了编译器去干。以上面的动态顺序表为例,假如没有类模板,我需要在一个程序中同时定义一个存储int型数据的顺序表,和存储double型数据的顺序表,因为没有类模板的话,动态顺序表的成员变量_pData的类型就只能时固定的intdouble,此时我们就只能写两个动态顺序表的类,将其中一个类的成员变量_pData设置成int类型,用来存储int型数据,将另一个类的成员变量_pData设置成double类型,用来存储double型数据。当程序中还需要一个存储其他类型数据的顺序表时,我们还得自己再增加类,这工作量可见一斑,长时间下去,不管你能不能忍受,你的头发必定受不了。而类模板的出现,就极大的缓解了我们头发的压力,我们只需要写一份动态顺序表的模板代码出来,当要用动态顺序表存储某类型的数据时,我们只需要把该类型告诉编译器,让编译器根据我们写的模板去实例化一个对应的动态顺序表类出来,存储该类型的数据。

四、非类型模板参数

前文提到的模板参数,准确的说应该叫做模板的类型形参,出现在模板参数列表中,跟在classtypename之后,用来表示一种类型;除此之外,模板还有另一种参数:非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。

📖非类型模板参数的使用场景

//静态栈
//#define M 100
template<class T, size_t N = 10>
class Stack
{
public:
	//一些成员函数
private:
	T _arr[N];
	//T _arr[M];
	int _top;
};
int main()
{
	Stack<int, 10> s1;//实例化一个栈可以存储10个整型数据
	Stack<double, 100> s2;//实例化出一个栈,可以存储100个double型数据
	return 0;
}

假设我们这里要实现一个静态的栈,即存储的数据量一经确认是无法扩容的,所以成员变量我们声明了一个数组_arr,在没有非类型模板参数的时候,设计静态的栈一般是通过#define来定义一个标识符常量,我们给这个常量一个初始值,就像上面代码中的M,最终设计出来的栈就能存储M个数据,但是一个程序中M只能被定义一次,当我们需要存储的多组数据,并且每组数据的数据量大不相同,就像上面的,s1需要存储10个int型数据,s2需要存储100个double型的数据,为了满足两者的需求,此时的M就只能大于100,这样对s1来说,就会造成极大的空间浪费。

非类型模板参数的引入就很好的解决了这个问题,我们可以根据实际的需求去传递参数,实例化出最符合自己需求的栈。

📖注意事项:

  • 非类型模板参数一定是一个常量,在类中不能对其进行修改。
  • 非类型模板参数的类型必须是整型,即intsize_tchar等。浮点型、类类型等是不允许作为非类型模板参数的。
  • 非类型模板参数必须在在编译阶段就能确认结果。

五、模板的特化

📖概念
在原模板的基础上,针对特殊类型惊醒特殊化的实现方式。模板特化分为函数模板特化类模板特化

📖为什么要有模板特化
通常情况下,使用模板可以实现一些与类型无关的代码,但是对于一些特殊的类型可能会得到一些错误的结果,此时就需要进行特殊处理。比如下面这个专门用来进行小于比较的函数模板:

template<class T>
bool Less(T left, T right)
{
	return left < right;
}
int main()
{
	int a = 10;
	int b = 20;
	cout << Less(a, b) << endl; // 可以比较,结果正确
	int* pa = &a;
	int* pb = &b;
	//希望通过传递a,b的地址去比较a,b的大小
	cout << Less(pa, pb) << endl; // 可以比较,结果错误
	return 0;
}

【C++杂货铺】模板,C++杂货铺,c++,java,开发语言,模板,热门
第一次,我们希望直接去比较ab的大小,于是就直接传递了ab,此时的模板参数T被隐式实例化为intLess函数中就进行的是两个整数的比较,比较的结果符合我们的预期。第二次,我们希望通过ab的地址去比较它们两个的大小,于是传递了ab的地址,此时的模板参数T被实例化为int*,因此Less函数就是进行两个地址的比较,并没有按照我们期望的那样,去比较两个地址中存储的数据大小,因此得到的结果也和我们预期的有所不同,此时我们就要对Less函数模板进行特化,让它能够满足我们的要求。

5.1 函数模板特化

📖注意事项:

  • 必须要先有一个基础的函数模板。
  • 关键字template后面接一对空的尖括号<>
  • 函数名后跟一对尖括号<>,尖括号中指定需要特化的类型。
  • 函数形参列表必须要和函数模板的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
//函数模板
template<class T>
bool Less(T left, T right)
{
	return left < right;
}
//对Less函数模板进行特化
template<>
bool Less<int*>(int* left, int* right)
{
	return *left < *right;
}

int main()
{
	int a = 10;
	int b = 20;
	cout << Less(a, b) << endl; // 可以比较,结果正确
	int* pa = &a;
	int* pb = &b;
	//希望通过传递a,b的地址去比较a,b的大小
	cout << Less(pa, pb) << endl; // 调用特化之后的版本,不走函数模板
	return 0;
}

【C++杂货铺】模板,C++杂货铺,c++,java,开发语言,模板,热门
bool Less<int*>(int* left, int* right)就是对Less函数模板的一个特化,当用户调用Less传递的是int*类型的参数时,会去调用特化后的版本,而不走函数模板。

小Tips:根据2.3小节的内容,一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出,像下面这样。

bool Less(int* left, int* right)
{
	return *left < *right;
}

这种实现简单明了,代码的可读性高,容易书写。

5.2 类模板特化

📖全特化
全特化就是将模板参数列表中所有的参数都确定化。

//类模板
template<class T1, class T2>
class Data
{
public:
	Data() { cout << "Data<T1, T2>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};

//对Data类模板进行特化
template<>
class Data<int, char>
{
public:
	Data() { cout << "Data<int, char>" << endl; }
private:
	int _d1;
	char _d2;
};

void TestVector()
{
	Data<int, int> d1;//使用类模板
	Data<int, char> d2;//使用全特化
}

【C++杂货铺】模板,C++杂货铺,c++,java,开发语言,模板,热门

📖偏特化
任何针对模板参数进行进一步条件限制设计出来的特化版本,叫做偏特化。偏特化有以下两种表现方式:

  • 部分特化

将类模板参数列表中的一部分参数进行特化。

//原类模板
template<class T1, class T2>
class Data
{
public:
	Data() { cout << "Data<T1, T2>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};

// 将第二个参数特化为int
template <class T1>
class Data<T1, int>
{
public:
	Data() {cout<<"Data<T1, int>" <<endl;}
private:
	T1 _d1;
	int _d2;
};

void TestVector()
{
	Data<int, int> d1;//使用部分特化
	Data<double, int> d2;//使用部分特化
	Data<char, int> d3;//使用部分特化
}

【C++杂货铺】模板,C++杂货铺,c++,java,开发语言,模板,热门
以上面为例,将原类模板的第一个参数仍旧使用模板T1,第二个模板参数特化成int型,此后只要第二个参数传的是int,就会走特化后的类。

  • 参数更进一步限制
//两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:
	Data() { cout << "Data<T1*, T2*>" << endl; }

private:
	T1 _d1;
	T2 _d2;
};

//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
	Data(const T1& d1, const T2& d2)
		: _d1(d1)
		, _d2(d2)
	{
		cout << "Data<T1&, T2&>" << endl;
	}

private:
	const T1& _d1;
	const T2& _d2;
};

void test2()
{
	Data<double, int> d1; // 调用特化的int版本
	Data<int, double> d2; // 调用基础的模板 
	Data<int*, int*> d3; // 调用特化的指针版本
	Data<double*, double*> d4; // 调用特化的指针版本
	Data<int&, int&> d5(1, 2); //调用特化的引用版本
	Data<char&, char&> d6('a', 'b'); //调用特化的引用版本
}

【C++杂货铺】模板,C++杂货铺,c++,java,开发语言,模板,热门

六、模板分离编译

📖什么是分离编译?
一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有的目标文件链接起来形成一个可执行文件的过程就叫做分离编译模式。

📖模板的分离编译
将模板的声明与定义分离,在头文件中进行声明,源文件中完成定义。

// a.h
template<class T>
T Add(const T& left, const T& right);

//a.cpp
#include "a.h"

template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}

//text.cpp
#include "a.h"

int main()
{
	Add(1, 2);
	Add(1.1, 2.2);
	return 0;
}

【C++杂货铺】模板,C++杂货铺,c++,java,开发语言,模板,热门
通过结果可以看出,上面的代码产生了error LNK2019链接错误。为什么会这样呢?下面我们来分析一下原因。首先我们需要明确头文件是不会参与编译的,只有以.cpp结尾的文件才能被编译,在预处理阶段,会把头文件的内容拷贝到包含了该头文件的.cpp文件中,其次编译器对工程中的多个源文件是分离开单独编译,编译阶段干的主要工作是按照语言的特性进行词法、语法、语义分析,检查无误后生成汇编代码,最终的到一个.obj结尾的目标文件。

📖先看没有使用模板的分离编译

// a.h

int Add(int left, int right);

//a.cpp
#include "a.h"

int Add(int left, int right)
{
	return left  + right
}

//text.cpp
#include "a.h"

int main()
{
	Add(1, 2);
	return 0;
}

以上面的代码为例,在编译text.cpp时,编译器不知道Add函数的实现,因为包含的a.h头文件中只有关于Add函数的一个声明,所以当编译器碰到对Add函数的调用时只是给出一个指示,指示链接器应该为它寻找Add函数的实现体。这也就是说test.obj中没有关于Add函数的任何一行二进制代码。

在编译a.cpp的时候,编译器找到了Add函数的实现。于是Add的实现,也就是对应的二进制代码出现在a.obj里。

链接时,链接器在a.obj中找到Add函数的实现代码(二进制)地址(通过符号表导出)。然后将test.obj中悬而未定的call XXX地址改成Add函数的实际地址。

📖模板需要实例化
然而,对于模板来说,模板函数的代码其实并不能直接编译生成二进制代码,其中要有一个“实例化”的过程,以下面的代码为例:

//test.cpp
template<typename T>
void Swap(T& left, T& right)
{
	T temp = left;
	left = right;
	right = temp;
}

int main()
{
	int i1 = 10;
	int i2 = 20;
	Swap(i1, i2);

	double d1 = 1.1;
	double d2 = 2.2;
	Swap(d1, d2);
}

如果在test.cpp文件中没有调用Swap函数,Swap就得不到实例化,从而test.obj中也就没有关于Swap的任何一行二进制代码。如果像上面代码中那样,调用了Swap(i1, i2);Swap(d1, d2);,此时test.obj中就有了Swap<int>Swap<double>两个函数的二进制代码段。

📖再来分析模板分离编译

//a.h
template<class T>
class A
{
public:
	void f();//这里只是声明
};

//a.cpp
template<class T>
void A<T>::f()//模板的实现
{
	//...实现
}

//test.cpp
int main()
{
	A<int> a;
	a.f();
	return 0;
}

编译器在执行到a.f()的时候并不知道A<int>::f的定义,因为它不在a.h里面,于是编译器只好寄希望于链接器,希望它能够在其他的.obj文件里面找到A<int>::f的实例,在本例中就是a.obj中,然而a.obj中是没有A<int>::f的二进制代码。因为C++标准明确规定,当一个模板不被用到的时候,他就不该被实例化出来,a.cpp中并有用到A<int>::f,所以实际上a.cpp编译出来的a.obj文件中关于A::f一行二进制代码也没有,因为没有进行任何的实例化,于是链接器就傻眼了,只好给出一个链接错误。

//a.cpp
//显式实例化
template
class A<int>;

如果在a.cpp中加上上面这段代码,a.f();就可以成功执行啦,上面这段代码会将模板专用化,于是,a.obj的符号导出表中就有了A<int>::f这个符号的地址,于是链接器就能够完成任务。但是如果在test.cpp文件中再定义一个A<double> b;,我们就需要在a.cpp中再加入相应的显式实例化,模板的分离编译显然是麻烦的。

📖建议
将声明和定义放到一个文件xxx.hpp里面或者xxx.h其实也是可以的。在同一个文件中对模板的声明和定义进行分离是不会出现链接错误的。

:模板分离编译这块参考了刘未鹏(pongba)大佬的文章,下面附上原文链接:传送门,感兴趣的小伙伴可以点进去看看。

七、模板总结

📖优点

  • 模板复用了代码,将本来需要人去完成的工作交给编译器去做,使人们可以更快的迭代开发,C++的标准模板库(STL)因此而产生。
  • 增强了代码的灵活性。

📖缺点

  • 模板会导致代码膨胀问题,也会导致编译时间变长。
  • 出现模板编译错误时,错误信息非常凌乱,不易定位错误。

结语:本篇文章只是基于语法层面对模板做了简单介绍,关于模板我们需要通过大量的实践,才能体会到模板的魅力以及它的语法细节。因此,在后面的文章中,我会通过模拟实现string和STL中的经典容器,来帮助大家更好的理解模板,感兴趣的小伙伴可以点下关注,更新时会第一时间告知。

模板使用的一个特例:

template<class container>
void Print(const container& con)
{
	typename container::const_iterator it = con.begin();
	while (it != con.end())
	{
		cout << *it << " ";
		it++;
	}
}

小Tips:在 container::const_iterator 之前需要加上 typename ,因为在实例化之前,编译器并不知道 const_iterator 是一个静态成员变量还是一个类型。加上 typename 则是明确的告诉编译器,这里的 const_iterator是一个类型。如果用 auto 则不需要加 typename。反正只要是取未实例化的类里面的内嵌类型都需要在前面加上 typename 告诉编译器这是一个类型。


好书推荐

在这里推荐两本我个人最近正在读的书,供暑假有意提升自己能力的小伙伴参考:
【C++杂货铺】模板,C++杂货铺,c++,java,开发语言,模板,热门
近期有购书需求的小伙伴可以直接点击下方书名前往选购!

NO.1《我看见了风暴:人工智能基建革命》
本书深入讲解了阿里、微软等业界巨头在人工智能技术领域的迭代历程,从框架设计、平台开发以及云基础设施等三个关键领域,对AI的发展历史进行详尽而深入的剖析,揭示对未来更远视野的洞察。

NO.2《趣话计算机底层技术》
本书的内容设计独特,通过富有吸引力的故事,深入浅出地解读了计算机中的CPU、存储、I/O、操作系统、系统编程以及安全六大主题。每一章都深入剖析了计算机的核心概念和关键技术,让读者在轻松的阅读时能够迅速提升自身计算机认知水平。

🎁彩蛋

【C++杂货铺】模板,C++杂货铺,c++,java,开发语言,模板,热门文章来源地址https://www.toymoban.com/news/detail-705554.html

到了这里,关于【C++杂货铺】模板的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++杂货铺】内管管理

    目录 🌈前言🌈 📁 C/C++中内存分布 📁 new 和 delete的使用 📁 new 和 delete的优点 📁 new 和 delete的原理  📂 operator new 和 operator delete函数  📂 内置类型  📂 自定义类型 📁 内存泄漏 📁 总结         欢迎收看本期【C++杂货铺】,本期内容讲解C++内存管理。包含了C++中内存

    2024年04月14日
    浏览(42)
  • 【C++杂货铺】详解string

    目录  🌈前言🌈 📁 为什么学习string 📁 认识string(了解) 📁 string的常用接口  📂 构造函数  📂 string类对象的容量操作  📂 string类对象的访问以及遍历操作​编辑  📂 string类对象的修改操作 📁 模拟实现string 📁 总结         欢迎观看本期【C++杂货铺】,本期内容

    2024年03月20日
    浏览(41)
  • 【C++杂货铺】拷贝构造函数

    📖 定义 拷贝构造函数 是构造函数的一个重载 ,它的本质还是 构造函数 ,那就意味着,只有在创建对象的时候,编译器才会自动调用它,那他和普通的构造函数有什么区别呢? 拷贝构造函数,是创建对象的时候,用一个已存在的对象,去初始化待创建的对象 。简单来说,

    2024年02月16日
    浏览(47)
  • 【C++杂货铺】运算符重载

    本文将以日期类为基础,去探寻运算符重载的特性与使用方法,下面先给出日期类的基础定义: 备注 :拷贝构造函数和析构函数,均可以不写,因为当前日期类的三个成员变量都是内置类型,没有动态申请空间,使用浅拷贝就可以。 📖 如何比较两个日期的大小? 现如今,

    2024年02月16日
    浏览(68)
  • 【C++杂货铺】缺省参数、函数重载

     缺省参数是 声明或定义函数时为函数的参数指定一个缺省值 。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。  上面代码在 fun 函数的形参部分给了缺省值10,这意味着在调用 fun 函数的时候可以传参,也可以不传参,如果传参了那形参

    2024年02月16日
    浏览(37)
  • 【C++杂货铺】详解list容器

    目录 🌈前言🌈 📁 介绍 📁 使用  📂 构造  📂 迭代器iterator  📂 capacity  📂 modifiers  📂 迭代器失效 📁 模拟实现  📂 迭代器的实现 📂 代码展示 📁 和vector的区别 📁 总结         欢迎收看本期【C++杂货铺】,本期内容将讲解STL中关于list的内容,会分为一下几个方

    2024年04月14日
    浏览(48)
  • 【C++杂货铺】C++介绍、命名空间、输入输出

     C语言是 结构化 和 模块化 的语言,适合处理 较小规模 的程序。对于复杂的问题,规模较大的程序,需要高度的抽象和建模时,C语言则不合适。为了解决软件危机,20世纪80年代,计算机界提出了 OOP (object oriented programming: 面向对象 )思想,支持面向对象的程序设计语言应

    2024年02月16日
    浏览(38)
  • 【C++杂货铺】string使用指南

    2024年02月14日
    浏览(43)
  • 【C++杂货铺】初识类和对象

    📖 面向过程 C语言是 面向过程的 ,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。以洗衣服这件事为例,下图是C语言完成洗衣服这件事的过程。 📖 面向对象 C++是 基于面向对象的 ,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完

    2024年02月16日
    浏览(49)
  • 【C++杂货铺】再谈类和对象

    在创建对象的时候,编译器通过调用构造函数,在构造函数体中,给对象中的各个成员变量一个合适的初值。 虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化, 构造函数体中的语句只能将其称为赋初值,而不能称作初

    2024年02月16日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包