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:// 构造函数,可以传入自定以的空间配置器,否则用默认的allocatorVector(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<< 重载

 

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#,入门教程(19)——循环语句(for,while,foreach)的基础知识

上一篇&#xff1a; C#&#xff0c;入门教程(18)——分支语句&#xff08;switch-case&#xff09;的基础知识https://blog.csdn.net/beijinghorn/article/details/124039953 一、for循环 当老师进入教室&#xff0c;从门口开始分别按行、列点名&#xff0c;看看哪位翘课&…

Elastic Stack(1):Elastic Stack简介

1 简介 ELK是一个免费开源的日志分析架构技术栈总称&#xff0c;官网https://www.elastic.co/cn。包含三大基础组件&#xff0c;分别是Elasticsearch、Logstash、Kibana。但实际上ELK不仅仅适用于日志分析&#xff0c;它还可以支持其它任何数据搜索、分析和收集的场景&#xf…

Docker(三)使用 Docker 镜像

作者主页&#xff1a; 正函数的个人主页 文章收录专栏&#xff1a; Docker 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01; 使用 Docker 镜像 在之前的介绍中&#xff0c;我们知道镜像是 Docker 的三大组件之一。 Docker 运行容器前需要本地存在对应的镜像&#x…

2018年认证杯SPSSPRO杯数学建模D题(第一阶段)投篮的最佳出手点全过程文档及程序

2018年认证杯SPSSPRO杯数学建模 对于投篮最佳出手点的探究 D题 投篮的最佳出手点 原题再现&#xff1a; 影响投篮命中率的因素不仅仅有出手角度、球感、出手速度&#xff0c;还有出手点的选择。规范的投篮动作包含两膝微屈、重心落在两脚掌上、下肢蹬地发力、身体随之向前上…

实战纪实 | 记一次攻防演练

看到一处登录后台&#xff0c;各种操作都尝试过无果&#xff0c;翻了一下js,看到一处文件上传接口泄露&#xff08;没图了&#xff0c;已经整改了&#xff09; 构造上传数据包&#xff0c;很nice,上传成功 直接连接webshell&#xff0c;搭建隧道进行内网穿透 翻看配置文件&…

Docker 容器连接

Docker 容器连接 前面我们实现了通过网络端口来访问运行在 docker 容器内的服务。 容器中可以运行一些网络应用&#xff0c;要让外部也可以访问这些应用&#xff0c;可以通过 -P 或 -p 参数来指定端口映射。 下面我们来实现通过端口连接到一个 docker 容器。 网络端口映射 …

Mac系统数据占用太多怎么清理 mac怎么清除下载的软件

在我们使用MacBook电脑的过程中&#xff0c;经常会遇到一个常见的问题&#xff0c;那就是储存空间不足。当我们的硬盘空间被占满的时候&#xff0c;系统的运行速度可能会变得缓慢&#xff0c;并且我们无法保存新的文件或者安装新的应用程序。要解决这个问题&#xff0c;清理缓存…

CSS 实现卡片以及鼠标移入特效

CSS 实现卡片以及鼠标移入特效 文章目录 CSS 实现卡片以及鼠标移入特效0、效果预览默认鼠标移入后 1、创建卡片组件2、添加样式3、完整代码 0、效果预览 默认 鼠标移入后 在本篇博客中&#xff0c;我们将探讨如何使用 CSS 来实现卡片组件&#xff0c;并添加鼠标移入特效&#…

leetcode 2114. 句子中的最多单词数

题目&#xff1a; 一个 句子 由一些 单词 以及它们之间的单个空格组成&#xff0c;句子的开头和结尾不会有多余空格。 给你一个字符串数组 sentences &#xff0c;其中 sentences[i] 表示单个 句子 。 请你返回单个句子里 单词的最多数目 。 解题方法&#xff1a; 1.遍历列表…

【SpringBoot】SpringBoot 项目初始化方法

github 搜索 springboot 模板 github 搜索 springboot 模板&#xff0c;拉取现成代码。 SpringBoot 官方的模板生成器 SpringBoot 官方的模板生成器&#xff08;https://start.spring.io/&#xff09; 在 IDEA 开发工具中生成 这里我修改成阿里的镜像主要是要使用 Java8。 …

springboot第50集:File类,IO流,网络编程,反射机制周刊

image.png FileReader、FileWriter的使用 FileInputStream、FileOutputStream的使用 image.png image.png image.png image.png image.png image.png image.png image.png image.png image.png image.png image.png 服务器内存优化是一个复杂的过程&#xff0c;通常需要综合考虑…

vue3开发移动端H5页面中video无交互自动播放完美解决方案

链接 官网&#xff1a;https://jsmpeg.com/ github&#xff1a;https://github.com/phoboslab/jsmpeg 官方例子&#xff1a;https://jsmpeg.com/perf.html 在线video转ts文件&#xff1a;https://convertio.co/zh/mp4-ts/ 踩坑 一、不用使用任何npm、yarn等安装 npm i jsmpe…