c++基础3

一 、构造函数的初始化列表

        可以指定成员对象的初始化方式

        构造函数的初始化列表是在 C++ 中用于初始化成员变量的一种机制。它在构造函数的参数列表之后,构造函数的函数体之前使用,并使用冒号 : 分隔。初始化列表可以用于给成员变量赋初值,而不是在构造函数的函数体内进行赋值操作。

二、类的成员方法和变量

 类的静态成员变量

         类的静态成员变量是属于类而不是属于类的实例的变量。它是通过使用 static 关键字声明的类成员。静态成员变量在类的所有实例之间共享,而不是每个实例拥有自己的一份。在.BSS段。

类的静态成员方法

        静态成员方法(或称为静态成员函数)是属于类而不是属于类的实例的方法。它们被声明为静态成员,并且可以通过类名直接调用,而不需要创建类的实例。

        静态成员方法和普通成员方法的区别: 普通成员方法是有this指针的,而静态成员方法没有 this指针,可以通过类名+类的成员函数进行调用,而不需要对象 

  常成员方法

常对象调不了普通方法,只能调用常方法,因为编译时,                                                              传入的this指针的类型时const CGoods *类型的

  指向类成员变量和类成员方法的指针(需要加类的作用域,静态成员方法或者成员变量则不需要)

1. 指向类成员变量的指针需要加上类的作用域,以及使用的时候需要加上对象,指定对象。

 2. 指向成员方法的指针,函数指针需要在类的作用域夏,并且调用的时候,需要加上对象

三、 C++的模板 

模板的意义:对类型进行参数化

 

 1. 函数模板和模板函数

        函数模板:所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。

        函数模板是一组函数的抽象描述,它不是一个实实在在的函数,函数模板不会编译成任何目标代码。函数模板必须先实例化成模板函数,这些模板函数再程序运行时会进行编译和链接,然后产生相应的目标代码。

        模板函数是函数模板的实例化(在函数的调用点)

        模板名+ 参数列表就是函数   

        模板的实参推演 =》 可以根据用户传入的实参类型,来推导出模板类型参数的具体类型

 

 2. 模板的特列化(特殊的实例化,不是编译器提供,而是开发者提供)

对于某些类型来说,依赖编译器默认实例化的模板代码,代码处理逻辑是错误的

对于字符串来说,不能直接用实例化后的a> b,而应该用strcmp

针对compare函数模板,提供const char *类型的特列化版本

 这三者不是重载关系,因为函数名都不相同。

compare("aaa" , "bbb");首先会调用普通函数(非模板函数)

compare<const char *> ("aaa", "bbb");肯定是先调用模板函数

编译器优先把compare处理成函数名字,没有的化,才去找compare模板

模板不能是一个文件定义,另一个文件使用,

因为#include包含的代码,在预编译的时候,会被展开 。

3. 模板的非类型参数 

四、容器的空间配置器allocator

        四件事情: 内存开辟、内存释放  、 对象构造、对象析构

        C++中的空间配置器(allocator)是一个用来管理内存分配的模板类。它用于封装不同的内存分配和回收策略,为C++的容器(如vector、list等)提供内存分配和回收的服务。

比如有一个容器vector:
#include <iostream>
using namespace std;
/*
这篇文章主要讲述空间配置器,所以实现的vector方法比较简单,
方法没有提供相应的带右值引用参数的移动函数,没有考虑过多
的异常情况
*/
template<typename T>
class Vector
{
public:
	// 构造函数
	Vector(int size = 0)
		:mcur(0), msize(size)
	{
		mpvec = new T[msize];
	}
	// 析构函数
	~Vector()
	{
		delete[]mpvec;
		mpvec = nullptr;
	}
	// 拷贝构造函数
	Vector(const Vector<T> &src)
		:mcur(src.mcur), msize(src.msize)
	{
		mpvec = new T[msize];
		for (int i = 0; i < msize; ++i)
		{
			mpvec[i] = src.mpvec[i];
		}
	}
	// 赋值重载函数
	Vector<T>& operator=(const Vector<T> &src)
	{
		if (this == &src)
			return *this;

		delete []mpvec;

		mcur = src.mcur;
		msize = src.msize;
		mpvec = new T[msize];
		for (int i = 0; i < msize; ++i)
		{
			mpvec[i] = src.mpvec[i];
		}
		return *this;
	}
	// 尾部插入数据函数
	void push_back(const T &val)
	{
		if (mcur == msize)
			resize();
		mpvec[mcur++] = val;
	}
	// 尾部删除数据函数
	void pop_back()
	{
		if (mcur == 0)
			return;
		--mcur;
	}
private:
	T *mpvec; // 动态数组,保存容器的元素
	int mcur; // 保存当前有效元素的个数
	int msize; // 保存容器扩容后的总长度

	// 容器2倍扩容函数
	void resize()
	{
	    /*默认构造的vector对象,内存扩容是从0-1-2-4-8-16-32-...的2倍方式
		进行扩容的,因此vector容器的初始内存使用效率特别低,可以使用reserve
		预留空间函数提供容器的使用效率。*/
		if (msize == 0)
		{
			mpvec = new T[1];
			mcur = 0;
			msize = 1;
		}
		else
		{
			T *ptmp = new T[2 * msize];
			for (int i = 0; i < msize; ++i)
			{
				ptmp[i] = mpvec[i];
			}
			delete[]mpvec;
			mpvec = ptmp;
			msize *= 2;
		}
	}
};
如果对上面容器调用:
// 一个简单的测试类A
class A
{
public:
	A() { cout << "A()" << endl; }
	~A() { cout << "~A()" << endl; }
};
int main()
{
	Vector<A> vec(10); // 10表示底层开辟的空间大小,但是却构造了10个A对象
	A a1, a2, a3;
	cout << "---------------" << endl;
	vec.push_back(a1);
	vec.push_back(a2);
	vec.push_back(a3);
	cout << "---------------" << endl;
	vec.pop_back(); // 删除a3没有对对象进行析构
	cout << "---------------" << endl;

	// vec容器析构时,内部只有2个有效的A对象,但是却析构了10次
	return 0;
}

运行上面的代码,打印结果如下:
A()
A()
A()
A()
A()
A()
A()
A()
A()
A() // 上面到这个是vector容器中,构造了10个对象
A() // 这里开始下面的三个A构造函数,是构造了a1, a2, a3三个对象
A()
A()
+++++++++++++++
+++++++++++++++
+++++++++++++++ // 这里有问题,vec.pop_back()删除末尾A对象,但是并没有进行析构调用,有可能造成资源泄露
~A()
~A()
~A() // 上面到这里的三个析构函数,析构了a1, a2, a3三个对象
~A()
~A()
~A()
~A()
~A()
~A()
~A()
~A()
~A()
~A() // 上面到这里的10个析构函数,是把vector容器中的对象全部进行析构
 

存在以下问题:

        1.  定义容器时Vector< A > vec(10),我们希望底层开辟可以容纳10个元素的空间,并不需要给我构造10个A对象,因为此时我还没有打算给容器添加数据,这么多构造函数的调用,纯粹是效率的浪费。
        2.从容器中删除元素时vec.pop_back(),这句代码的意思是删除了容器末尾的对象A,但是并没有调用A对象的析构函数,如果A对象占用了外部资源,那么资源的释放代码肯定在A的析构函数里面,这样就造成了资源泄露的问题。
        3.vec容器在出函数作用域析构的时候,并没有析构有效的A对象,其实上面代码中,最终vec容器只有两个我们放入的有效对象a1和a2,a3被删除了,应该只析构两次就可以,但是却析构了10次,不合理。

自定义一个空间配置器
// 自定义空间配置器
template<typename T>
struct myallocator
{
	// 开辟内存空间
	T* allocate(size_t size) 
	{
		return (T*)::operator new(sizeof(T)*size);// 相当于malloc分配内存
	}
	// 释放内存空间
	void deallocate(void *ptr, size_t size)
	{
		::operator delete(ptr, sizeof(T)*size);// 相当于free释放内存
	}
	// 负责对象构造
	void construct(T *ptr, const T &val)
	{
		new ((void*)ptr) T(val);// 用定位new在指定内存上构造对象
	}
	// 负责对象析构
	void destroy(T *ptr)
	{
		ptr->~T();// 显示调用对象的析构函数
	}
};

C++ STL库中vector容器的类模板定义头

template<class _Ty,
	class _Alloc = allocator<_Ty>>
	class vector
#include <iostream>
using namespace std;

// 自定义空间配置器
template<typename T>
struct myallocator
{
	// 开辟内存空间
	T* allocate(size_t size) 
	{
		return (T*)::operator new(sizeof(T)*size);// 相当于malloc分配内存
	}
	// 释放内存空间
	void deallocate(void *ptr, size_t size)
	{
		::operator delete(ptr, sizeof(T)*size);// 相当于free释放内存
	}
	// 负责对象构造
	void construct(T *ptr, const T &val)
	{
		new ((void*)ptr) T(val);// 用定位new在指定内存上构造对象
	}
	// 负责对象析构
	void destroy(T *ptr)
	{
		ptr->~T();// 显示调用对象的析构函数
	}
};

/*
给Vector容器的实现添加空间配置器allocator
*/
template<typename T, typename allocator = myallocator<T>>
class Vector
{
public:
	// 构造函数,可以传入自定以的空间配置器,否则用默认的allocator
	Vector(int size = 0, const allocator &alloc = allocator())
		:mcur(0), msize(size), mallocator(alloc)
	{
		// 只开辟容器底层空间,不构造任何对象
		mpvec = mallocator.allocate(msize);
	}
	// 析构函数
	~Vector()
	{
		// 先析构容器中的对象
		for (int i = 0; i < mcur; ++i)
		{
			mallocator.destroy(mpvec+i);
		}
		// 释放容器占用的堆内存
		mallocator.deallocate(mpvec, msize);
		mpvec = nullptr;
	}
	// 拷贝构造函数
	Vector(const Vector<T> &src)
		:mcur(src.mcur)
		, msize(src.msize)
		, mallocator(src.mallocator)
	{
		// 只开辟容器底层空间,不构造任何对象
		mpvec = mallocator.allocate(msize);
		for (int i = 0; i < mcur; ++i)
		{
			// 在指定的地址mpvec+i上构造一个值为src.mpvec[i]的对象
			mallocator.construct(mpvec+i, src.mpvec[i]);
		}
	}
	// 赋值重载函数
	Vector<T> operator=(const Vector<T> &src)
	{
		if (this == &src)
			return *this;

		// 先析构容器中的对象
		for (int i = 0; i < mcur; ++i)
		{
			mallocator.destroy(mpvec + i);
		}
		// 释放容器占用的堆内存
		mallocator.deallocate(mpvec, msize);

		mcur = src.mcur;
		msize = src.msize;
		// 只开辟容器底层空间,不构造任何对象
		mpvec = mallocator.allocate(msize);
		for (int i = 0; i < mcur; ++i)
		{
			// 在指定的地址mpvec+i上构造一个值为src.mpvec[i]的对象
			mallocator.construct(mpvec + i, src.mpvec[i]);
		}
		return *this;
	}
	// 尾部插入数据函数
	void push_back(const T &val)
	{
		if (mcur == msize)
			resize();
		mallocator.construct(mpvec + mcur, val);
		mcur++;
	}
	// 尾部删除数据函数
	void pop_back()
	{
		if (mcur == 0)
			return;
		--mcur;
		// 析构被删除的对象
		mallocator.destroy(mpvec + mcur);
	}
private:
	T *mpvec; // 动态数组,保存容器的元素
	int mcur; // 保存当前有效元素的个数
	int msize; // 保存容器扩容后的总长度
	allocator mallocator; // 定义容器的空间配置器对象

	// 容器2倍扩容函数
	void resize()
	{
		if (msize == 0)
		{
			mpvec = mallocator.allocate(sizeof(T));
			mcur = 0;
			msize = 1;
		}
		else
		{
			T *ptmp = mallocator.allocate(2 * msize);
			for (int i = 0; i < msize; ++i)
			{
				mallocator.construct(ptmp + i, mpvec[i]);
			}
			// 先析构容器中的对象
			for (int i = 0; i < msize; ++i)
			{
				mallocator.destroy(mpvec + i);
			}
			// 释放容器占用的堆内存
			mallocator.deallocate(mpvec, msize);
			mpvec = ptmp;
			msize *= 2;
		}
	}
};
// 一个简单的测试类A
class A
{
public:
	A() { cout << "A()" << endl; }
	~A() { cout << "~A()" << endl; }
};
int main()
{
	Vector<A> vec(10); // 此处只开辟内存,没有构造任何对象
	A a1, a2, a3;
	cout << "+++++++++++++++" << endl;
	vec.push_back(a1);
	vec.push_back(a2);
	vec.push_back(a3);
	cout << "+++++++++++++++" << endl;
	vec.pop_back(); // 删除a3并析构a3对象
	cout << "+++++++++++++++" << endl;

	// vec容器析构时,内部只有2个有效的A对象,析构了2次,正确
	return 0;
}

        通过打印可以看到,最开始实现的容器,我们提到的这三个问题:

        定义容器时Vector< A > vec(10),我们希望底层开辟可以容纳10个元素的空间,并不需要给我构造10个A对象,因为此时我还没有打算给容器添加数据,这么多构造函数的调用,纯粹是效率的浪费。
        从容器中删除元素时vec.pop_back(),这句代码的意思是删除了容器末尾的对象A,但是并没有调用A对象的析构函数,如果A对象占用了外部资源,那么资源的释放代码肯定在A的析构函数里面,这样就造成了资源泄露的问题。
        vec容器在出函数作用域析构的时候,并没有析构有效的A对象,其实上面代码中,最终vec容器只有两个我们放入的有效对象a1和a2,a3被删除了,应该只析构两次就可以,但是却析构了10次,不合理。
        现在都通过空间配置器allocator解决了,仔细对比最开始的Vector和修改后带空间配置器版本的Vector的代码实现,体会allocator在容器中的具体使用。

五、运算符重载

         这里并没有在两个原来的对象上进行修改,而是创建了一个新的对象 

下面这个+20相当于 构造了一个临时对象(类型强转)

 下面这个这错误的:

 然后就做以下操作:写一个全局的运算符重载,然后写个友元

 前置++ 和 后置++

 cout<< 重载

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/332458.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ycsb压测mongodb

下载解压 https://github.com/brianfrankcooper/YCSB/releases/download/0.17.0/ycsb-mongodb-binding-0.17.0.tar.gz tar -zxvf ycsb-mongodb-binding-0.17.0.tar.gzycsb提前已经在workload文件夹下准备好了几个压测场景分别对应workload[a:f] workloads/workloada 样例 …

Docker-harbor私有仓库部署与管理

目录 实验前准备部署Docker-compose服务部署Harbor服务修改配置文件创建项目 客户端测试修改客户端配置测试 维护管理Harbor创建项目创建用户查看日志修改Harbor.cfg配置文件补充 实验前准备 Harbor服务器&#xff1a;192.168.188.11 docker-ce、docker-compose、harbor-offli…

【计算机网络】OSI七层模型与TCP/IP四层模型的对应与各层介绍

1 OSI七层模型与TCP/IP四层模型对应 2 OSI七层模型介绍 OSI&#xff08;Open Systems Interconnection&#xff09;模型是一个由国际标准化组织&#xff08;ISO&#xff09;定义的七层网络体系结构&#xff0c;用于描述计算机网络中的通信协议。每一层都有特定的功能&#xff…

C#,入门教程(38)——大型工程软件中类(class)修饰词partial的使用方法

上一篇&#xff1a; C#&#xff0c;入门教程(37)——优秀程序员的修炼之道https://blog.csdn.net/beijinghorn/article/details/125011644 一、大型&#xff08;工程应用&#xff09;软件倚重 partial 先说说大型&#xff08;工程应用&#xff09;软件对源代码的文件及函数“…

2023 China DevOpsDays(DOD) DXCon 国际数字化转型与创新管理企业峰会:核心内容与学习收获(附大会核心PPT下载)

随着科技的飞速发展&#xff0c;数字化转型已成为企业持续发展的必经之路。2023年的China DevOpsDays & DXCon国际数字化转型与创新管理企业峰会&#xff0c;汇集了业界顶尖的专家、学者和企业领袖&#xff0c;共同探讨数字化转型的最新趋势和实践。本文将深入剖析大会的核…

多功能隐写融合

最近尝试了一个融合了多功能隐写的项目&#xff0c;参考了一些现有的资料和相关的开源库&#xff0c;并最终集成到了可视化UI当中。这篇文章讲述了实现的几项隐写技术的原理以及最终呈现的效果&#xff0c;后续会在“隐私保护”以及“ui”的专栏中继续更新详细的ui设计思路以及…

Asp .Net Core 系列:集成 Ocelot+Nacos+Swagger+Cors实现网关、服务注册、服务发现

文章目录 简介什么是 Ocelot ?什么是 Nacos ?什么是 Swagger ?什么是 Cors ? Asp .Net Core 集成 Ocelot网关集成 Nacos下游配置 Nacos配置跨域&#xff08;Cors&#xff09;网关和微服务中配置Swagger效果 简介 什么是 Ocelot ? Ocelot是一个开源的ASP.NET Core微服务网…

某C2鸡肋漏洞分析:你的CS安全吗?

CobaltStrike是一个知名的红队命令与控制框架&#xff0c;采用Beacon <-> TeamServer <-> Client架构。TeamServer存在受限路径穿越写文件与反序列化漏洞&#xff0c;可以被认证后客户端恶意利用。Client存在反序列化漏洞&#xff0c;可以被RogueCS攻击。 山寨威胁…

PyTorch各种损失函数解析:深度学习模型优化的关键(2)

目录 详解pytorch中各种Loss functions mse_loss 用途 用法 使用技巧 注意事项 参数 数学理论公式 代码演示 margin_ranking_loss 用途 用法 使用技巧 注意事项 参数 数学理论公式 代码演示 multilabel_margin_loss 用途 用法 使用技巧 注意事项 参数 …

【BERT】详解

BERT 简介 BERT 是谷歌在 2018 年时提出的一种基于 Transformer 的双向编码器的表示学习模型&#xff0c;它在多个 NLP 任务上刷新了记录。它利用了大量的无标注文本进行预训练&#xff0c;预训练任务有掩码语言模型和下一句预测&#xff0c;掩码语言模型指的是随机地替换文本中…

红外遥控,按键事件

问&#xff1a;android tv的遥控功能是如何实现的 答&#xff1a; Android TV 的遥控功能是通过红外遥控器或蓝牙遥控器来实现的。下面分别介绍这两种遥控器的工作原理&#xff1a; 红外遥控器&#xff1a; 红外遥控器是最常见的 Android TV 遥控器类型之一。 红外遥控器通…

Electron中苹果支付 Apple Pay inAppPurchase 内购支付

正在开发中&#xff0c;开发好了&#xff0c;写一个完整详细的过程&#xff0c;保证无脑集成即可 一、先创建一个App 一般情况下&#xff0c;在你看这篇文章的时候&#xff0c;说明你已经开发的app差不多了。 但是要上架app到Mac App Store&#xff0c;则要在appstoreconnect…

ROS第 9 课 编写简单的服务端 Server

文章目录 第 9 课 编写简单的服务端 Server1.创建服务器代码2.运行服务器节点 第 9 课 编写简单的服务端 Server 1.创建服务器代码 注意&#xff1a;在创建服务器代码之前&#xff0c;需要先创建工作空间和功能包&#xff0c;具体操作过程可前往目录“第4课 创建工作空间与功能…

14 STM32 - IIC (时序图+软件源码)

14.1 IIC简介 IIC&#xff08;Inter-Integrated Circuit&#xff09;&#xff0c;中文集成电路总线&#xff0c;是一种串行通信总线&#xff0c;使用多主从架构。I2C串行总线一般有两根信号线&#xff0c;一根是双向的数据线SDA&#xff0c;另一根是时钟线SCL。所有接到I2C总线…

VUE--组件的生命周期及其基本应用

VUE的生命周期 上图是实例生命周期的图表&#xff0c;需要注意以下几个重要时期&#xff1a; 创建期&#xff1a;beforeCreated、created 挂载期&#xff1a;beforeMount、mounted 更新期&#xff1a;beforeUpdate、updated 销毁期&#xff1a;beforeUnmount、unmounted 生命周…

云原生演进中的AI算力高效使用

0 1 云原生技术的普及与发展 云原生技术是一种基于容器技术的轻量级、高可用的应用架构&#xff0c;具有弹性扩展、快速部署、统一管理等特点。随着企业对敏捷开发和快速迭代的需求不断增加&#xff0c;云原生技术的普及与发展已成为不可逆转的趋势。 图1. 云原生技术发展之路…

软件测试阶段简介_单元测试、集成测试、配置项测试、系统测试

文章目录 前言一、软件测试“V”模型二、单元测试三、集成测试四、配置项测试五、系统测试总结 前言 一般来说&#xff0c;按照软件的研制阶段划分&#xff0c;软件测试可分为单元测试、集成测试、配置项测试、系统测试等。本文将对上述各测试阶段进行逐一介绍。 一、软件测试…

元宇宙:智慧城市建设的未来引擎与价值之源

在21世纪的技术大潮中&#xff0c;元宇宙的出现无疑是一场革命&#xff0c;其独特的概念与价值已经引发了全球范围内的关注。 作为新兴科技的前沿&#xff0c;元宇宙为智慧城市建设带来了无限的可能性和价值&#xff0c;有望成为未来城市发展的核心动力。 元宇宙&#xff0c;这…

【RPC】动态代理:面向接口编程,屏蔽RPC处理流程

一、背景 如果我问你&#xff0c;你知道动态代理吗&#xff1f; 你可能会如数家珍般地告诉我动态代理的作用以及好处。那我现在接着问你&#xff0c;你在项目中用过动态代理吗&#xff1f;这时候可能有些人就会犹豫了。那我再换一个方式问你&#xff0c;你在项目中有实现过统一…

【RT-DETR有效改进】轻量级网络ShuffleNetV2(附代码+修改教程)

前言 大家好&#xff0c;这里是RT-DETR有效涨点专栏。 本专栏的内容为根据ultralytics版本的RT-DETR进行改进&#xff0c;内容持续更新&#xff0c;每周更新文章数量3-10篇。 专栏以ResNet18、ResNet50为基础修改版本&#xff0c;同时修改内容也支持ResNet32、ResNet101和PP…