C++之拷贝构造、拷贝赋值

2023-11-11

拷贝构造

class Stu{
public:
    int no;
    string name;
    int age;
public:
    Stu(int no=10086, string name="jin", int age = 18):no(no), name(name), age(age){}
	//拷贝构造函数:由同类型的对象构造一个新的副本对象
    Stu(const Stu &s){
		no = s.no;
        name = s.name;
        agr = s.age
    }
};
int main()
{
	Stu s1 = (10010, "jink", 100);
    
    //拷贝构造,但是调用的并不是Stu这个构造函数,而是拷贝构造函数
    Stu s2(s1);
}

定义

由已经存在的同类型对象构造一个新的副本对象,调用的是拷贝构造函数、

  • 拷贝构造函数的形式是固定不变的:

    class 类名{
        类名(const 类名& 对象){
    		
        }
    };
    
本质

拷贝构造函数也是构造函数,是一个特殊

  • 特殊在参数是一个同类型的对象引用

如果在实现一个类时 没有为这个类添加拷贝构造函数,则编译器会提供一个默认的

默认的拷贝构造函数是:全员复制(逐字节拷贝)副本和元对象一摸一样

程序员可以俺自己的需求构造拷贝构造函数,构造后编译器不再提供默认拷贝构造函数

使用时机
  • 非引用传递对象
  • 放入容器的对象都是通过拷贝构造函数
  • 用存在的对象构造新的同类型的对象
调用语法
  • 类名 对象1 = 对象2;
  • 类名 新对象(老对象);
非默认拷贝构造函数的使用时机
  • 如果需要实现深拷贝时则需要自己实现拷贝构造函数 默认的拷贝构造函数是浅拷贝
  • 有指针指向动态内存时一般需要实现拷贝构造函数
  • 浅拷贝——按照字节拷贝 按字节复制 拷贝对象和原对象一模一样
    • 对于指针,只拷贝内存地址
  • 深拷贝——对于普通数据而言没有深拷贝一说,拷贝指针所指的内容
    • 对于指针,重新申请内存,把原指针指向的内容拷贝的新的内存空间中

所以当有指针指向动态内存时,一般需要使用独立构造的深拷贝

class Ptr{
private:
    int *ptr;
public:
    Ptr(const Ptr &pp){}
}

拷贝赋值函数

拷贝赋值的时点

当两个已经存在的同类型对象之间的相互赋值 ,调用的就是拷贝赋值

拷贝赋值函数形如:

class 类名{
	类名& operator(const 类名 &对象名){}
}

如果一个类没有实现拷贝赋值函数,编译器提供默认的拷贝赋值函数,且为浅拷贝

若需要实现深拷贝,则需要程序员手动提供

一般来说:遵循三/五原则

  • C++11之前为三:析构,拷贝构造,拷贝赋值,要么全部自己提供 ,要么全用默认
  • C++11之后为五:析构,拷贝构造, 拷贝赋值,移动构造, 移动赋值

实现简单的string类

class String{
public:
    //用C风格的字符串构造 String对象
    String(const char *s = NULL):
    str(strcpy(new char[s?strlen(s)+1:1, s?s:"")){}
    ~String(void){
        if(str != nullptr){
            delete[] s;
            str = nullptr;
        }
    }
	//拷贝构造函数   用同类型的对象拷贝构造一个同类型的副本对象
    String (const String &s):
        str(strcpy(new char[strlen(s.str)+1, s.str){}
    String &operator=(const String& ss){
        if(this != s){
            String _tmp(s);
            swap(str,_tmp.str);
	//看不到释放内存  但它有释放  delete [] _tmp.str
	//_tmp对象生命周期在语句块结束之后 到期  自动调用析构 
        }
        return *this;
    } 
    size_t length(void)const{
        return strlen(str);
    }
    const String *c_str(void)const{
        return str;
    }
prevate:
    char *str;
};

封装一个栈以及简单测试:

#include <bits/stdc++.h>
using namespace std;

class Stack{
public:
    Stack(int cap, int sizes = 0):
        cap(cap){
            elems = new int[cap];
        }
        //elems(new int[cap])
    ~Stack(void){
        if(sizes != 0){
            delete[] elems;
            elems = nullptr;
        }
    }
    Stack(Stack &stack){
        cap = stack.capacity();
        sizes = stack.size();
        int *Elem =  new int[stack.cap];
        for(int i = 0; i < stack.size(); i++){
            Elem[i] = stack.elems[i];
        }
    }
    Stack &operator = (Stack &stack){
        if(this != &stack){
            Stack _tmp(stack);
            swap(elems, _tmp.elems);
            cap = stack.capacity();
            sizes = stack.size();
        }
        return *this;
    }
    void push(int elem){
        if(full()){
            throw out_of_range("full");//抛出异常<stdexcept>
        }
        elems[sizes++] = elem;
    }
    int pop(void){
        if(empty()){
            throw out_of_range("empty");
        }
        return elems[--sizes];
    }
    bool full(){
        return cap == sizes;
    }
    bool empty(){
        return sizes == 0;
    } 
    int top(void){
        if(empty()){
            throw out_of_range("empty");
        }
        return elems[sizes-1];
    }
    int size(void){
        return sizes;
    }
    int capacity(void){
        return cap;
    }
private:
    int cap;
    int sizes;
    int *elems;

};

int main()
{
    Stack stack(10);
    for(int i = 0; i < 10; i++){
        stack.push(i);
    }
    for(int i = 0; i < 10; i++){
        cout<<stack.pop()<<endl;
    }
}
默认的无参构造
  • 默认无参构造:在一个类没有实现构造函数到时候编译器自动提供的,
    • 如果需要显式调用有参构造函数,需要提供成员名和实参
  • 默认拷贝构造函数:会在初始化列表中调用类类型成员的拷贝构造函数
    • 需要实现对成员的拷贝
  • 默认拷贝赋值函数:会在函数体中调用类类型的成员的拷贝赋值函数
    • 需要实现对成员的拷贝
  • 默认析构函数:会自动调用类类型 的成员的析构函数

对一个空类至少有以下几个成员函数

  1. 无参构造
  2. 拷贝构造
  3. 拷贝赋值
  4. 析构函数
  5. & 取值函数 T* operator&(void)
  6. & 常取址函数 const T* operator&(void)const
  • 如果在一个类中只提供拷贝构造函数,则其只有拷贝构造函数一个成员函数

移动构造

class 类名{
public:
    类名(类名&& obj){
        
    }
};
  • 用一个存在的对象来构造一个同类型新的对象,但是旧的对象是马上就会消亡的,为了效率着想,就没有必要调用拷贝构造(造成资源浪费,复制一份,析构一份),这个时候就可以选择移动构造,把旧对象的资源移动(赠予)新的对象,需要保证旧的对象能够正常析构

  • 旧对象不能再使用了

移动赋值

  • 用一个存在的对象给另外一个存在的对象的进行赋值,正常情况下,被赋值的资源应该析构,然后把另外一个对象的资源拷贝一份给被赋值的对象,但是如果=右边的对象马上就会消亡时,为了效率着想,就没有必要拷贝一份给被赋值的对象,直接把资源交给(转移给)被赋值的对象
  • =右边的对象不能再使用了
class 类名{
public:
    类名& operator=(类名&& obj){
        
    }
};

单例模式

模式:总结出的开发套路,能减少代码重复度,提高代码可靠性和效率,且实现特定的功能

单例模式:

定义:一个类只能创建一个对象

  • 懒汉模式
    • 不到万不得已不会动,只有饿了才会找吃的
    • 单例模式的对象只有有需求时才会创建
class SingleTon{
public:
    static SingleTon* getInstance(void){
   		if(ps == nullptr){
			ps = new SingleTon();
        }
        return ps;
    }
    void realse(void){
		--cnt;
        if(cnt == 0){
            delete ps;
            ps = nullptr;
        }
    }
    ~SingleTon(){
        
    }
private:
    int no;
    string name;
private:
    SingleTon(){}
    SingleTon(const SingleTon& s){}
    
    static SingleTon *ps;//指向唯一实例的指针
    static int cnt;
};
SingleTon SingleTon::ps = nullptr;
int SingleTon::cnt = 0;
int main()
{
	SingleTon *p1 = SingleTon::getIntance();
}

优缺点

  • 只有有需求时才会创建,节省内存空间,用完了可以及时释放
  • 线程不安全 两个线程同时去创建可能会创建多份实例,所以需要考虑线程同步,线程同步效率变低
  • 饿汉模式
    • 很饿,需要时刻准备吃的
    • 该模式下单例模式对象一直存在,无论是否有需求
class SingleTon{
public:
    static SingleTon* getInstance(void){
        return s;
    }
    ~SingleTon(){}
private:
    int no;
    string name;
    static SingleTon s;
private:
    SingleTon(){}
    SingleTon(const SingleTon& s){}
    
    static SingleTon s;//唯一实例
};
SingleTon SingleTon::s;

优缺点

  • 不管是否有需求,一直占用内存,浪费内存空间
  • 饿汉模式是在程序加载阶段就去实例,能够保证只有一个线程,线程安全的
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++之拷贝构造、拷贝赋值 的相关文章

  • 使用 testcontainer 作为 Dockerfile 的一部分运行测试

    我的 dockerfile 看起来像这样 FROM maven 3 jdk 11 slim COPY pom xml COPY src src RUN mvn clean install 这意味着构建的一部分是单元测试的执行 一些单元测试使
  • 获取不带波形符的泛型类名称[重复]

    这个问题在这里已经有答案了 我正在尝试获取类型名称T使用这个 typeof T Name 班级名称是ConfigSettings 而不是返回ConfigSettings它正在返回ConfigSettings 1 有什么具体原因吗 我怎样才能
  • 无法使用 Jsoup HTML 解析器 Java 实现某些功能

    我无法使用 Jsoup Java 库解析以下场景的一些文本 1 This is b My Text b some other b b text as well b b b non empty tag1 b other text 预期输出 s
  • 会员提供商使用还是不使用?

    我正在开发一个使用 Facebook 的网站 现在为了管理用户我想使用MembershipProvider并选择开发一个定制的会员提供商 我的问题是我的数据库架构与标准成员资格架构不匹配 并且提供的用于覆盖的函数采用与我预期不同的参数 例如
  • DataGridView小数不排序

    好吧 我有一个 DataGridView 它的数据绑定如下 dataGridViewChartOre AutoGenerateColumns false dataGridViewChartOre DataSource xml GetOreC
  • 从 Apache Kafka 中的主题删除消息

    所以我是 Apache Kafka 的新手 我正在尝试创建一个简单的应用程序 以便我可以更好地理解 API 我知道这个问题在这里被问了很多 但是如何清除存储在主题上的消息 记录 我看到的大多数答案都说要更改消息保留时间或删除并重新创建主题
  • 获取进程的所有 DLL

    我想获取为给定进程加载的所有 dll 的列表 我目前正在使用 NET框架4 0 我知道有一个bug https connect microsoft com VisualStudio feedback details 546430 syste
  • 我可以使用本机系统窗口作为父窗口使 JDialog 成为模式吗?

    我有一个 JDialog 窗口 我需要使其成为模态窗口 但父窗口不是 Java 窗口 而是本机 Windows 操作系统窗口 是否可以 不 你不能 您甚至无法不仅引用本机窗口 甚至无法引用运行在其他 JVM 中的 java 应用程序创建的窗
  • vs2010 c++ 通过debug查看指针内容

    我正在使用 Vs2010 c 处理 2D 数组 我从一维指针开始 并使用操作 如下 class CMatrix void clear public int nRows int nCols short MyMat CMatrix CMatri
  • JavaFX颜色选择器的语言

    有没有办法改变语言ColorPicker的文本 例如 自定义颜色 当前颜色 新颜色 色相 饱和度 亮度 不透明度 保存 使用 取消 编辑 以下答案适合那些需要更多内容的人exotic语言 如果您使用其中之一 de es fr it ja k
  • cmake 包括其他目录中的 h 文件

    我在 cmake 项目下进行测试时遇到问题 我的项目是这样安排的 TerrainMap PointAccumulator heightQuadGrid Test 在 TerrainMap 目录中 CMakeLists txt 文件简单地概述
  • 为什么 g++ 在编译的二进制文件中存储类名?

    我注意到如果我跑strings在我编译的程序上g 输出包含它使用的各种类的名称 该程序是用 O3并且没有 g or p 并且当我剥离二进制文件时 类名仍然存在 我想知道为什么有必要g 将此信息存储在二进制文件中 出现的类名似乎都是使用虚函数
  • 使用客户端 hello 消息进行 TLS 协议检测

    我需要检测网络流量中的 https 数据包 到目前为止 我将所有 443 标记为 https 但我不想再在这种情况下使用端口信息 检查客户端问候消息是否足够 Check 22 and version info 0300 0301 or 03
  • 如何重写(重新实现)QFileSystemModel 中的成员函数

    我已经为此苦苦挣扎了一段时间 Qt s QFileSystemModel由于图标获取算法非常糟糕 在获取数百个文件时速度非常慢 我想完全禁用图标 它们被提取到QFileSystemModel data方法不是虚拟的 QFileSystemM
  • 我可以创建一个 List> 吗?

    我正在尝试创建一个列表WeakReference使用 4 5 泛型实现 这样我就可以避免类型检查和转换WeakReference目标 但 WeakReference
  • Eclipse CDT C/C++:包含另一个项目的头文件

    我在 Eclipse CDT 中有两个 C 项目main and shared In shared我有一个名为calc h 我想在中使用这个标头main 所以我做了以下事情 added include calc h到相关文件main In
  • C++11 中引入了哪些重大更改?

    我知道 C 11 中至少有一项更改会导致一些旧代码停止编译 引入explicit operator bool 在标准库中 替换旧实例operator void 诚然 这将破坏的代码可能是一开始就不应该有效的代码 但它仍然是一个破坏性的变化
  • Java中不同格式的字符串解析为日期

    我想转换String to Date以不同的格式 例如 我从用户那里得到 String fromDate 19 05 2009 i e dd MM yyyy format 我想转换这个fromDate作为日期对象 yyyy MM dd fo
  • 如何在 C# 中将 json 转换为平面结构

    我正在尝试用 C 编写函数 将 JSON 转换为键 值对 它应该支持数组 例如下面的 JSON title title value components component id id1 menu title menu title1 tit
  • JVM锯齿状空闲进程

    我目前正在进行一项涉及 JVM 及其内存使用工作原理的研究 我不明白的是 JVM在空闲时用什么填充它的内存 只是为了在堆几乎达到时释放它 为什么使用的内存不只有一条平线 顺便说一句 这个 java 应用程序托管在 glassfish 上 但

随机推荐

  • 浅谈BERT/Transformer模型的压缩与优化加速

    作者 姚益武 单位 阿里巴巴集团 研究方向 AI算法与工程架构 前言 BERT Transformer 结构及其变体 已成为自然语言处理 NLP 语音识别 ASR 等领域的主流序列建模结构 并且 相比于卷积操作的局部特征提取能力 以及平移不
  • 【Python】ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any

    一 问题描述 在使用Python的判断语句的时候 data2 data2 Month 11 and data2 Day 11 我的本意是想找出11月11日的数据 运用上面的代码 却得到了报错 报错信息 ValueError The trut
  • [QT编程系列-38]:数据存储 - SQLite数据库存储与操作

    目录 1 SQLite数据库概述 1 1 简介 1 2 SQLite不支持网络连接 1 3 SQLite不需要安装MySQL Server数据库 1 4 SQLite性能 1 5 SQLite支持的数据条目 2 SQLite操作示例 3 Q
  • 找不到插件 ‘org.springframework.boot:spring-boot-maven-plugin:‘问题

    出现pom文件找不到插件 org springframework boot spring boot maven plugin 问题 可能是因为版本没有绑定好 去一级父类依赖找对应的插件版本 在pom文件中加上 把父类的version加到po
  • 1小时学会CSS-下

    今天给大家分享的内容包含CSS 盒子模型 CSS 标准布局 CSS 浮动布局 并以案列进行详细说明 一 CSS 盒子模型 CSS 将所有元素都当成盒子 CSS布局其实就是如何堆放盒子 组成 content 内容 gt padding 内边距
  • 【c语言】小程序游戏——飞机游戏(二)

    在接下来 我们需要对飞机添加一些属性 1 利用键盘控制飞机的移动 2 按空格键可以发射激光 NO 1 利用键盘控制飞机的移动 首先我们需要了解的是 如果想要通过按键来控制飞机的移动 那么我们具体要怎么控制呢
  • Verilog(4)系统任务monitor,time,stop,random等,编译预处理语句

    调试用系统任务和常用编译预处理语句 用于调试和差错的系统任务 以及编写模块时的预处理语句 系统任务 monitor 提供了监控和输出参数列表中表达式或变量值的功能 参数列表中输出控制格式字符串和输出表列的规则和 d i s p l a
  • java基础之replace,replaceAll

    走在乡间的小路上 回首过往 以下代码结果都是显示在Console框中 所以 n会被解析成换行 n只显示 n 所以看到结果换行其实是输出了 n 看到输出 n 其实输出的是 n replace和replaceAll是编程中经常用到的替换函数 成
  • 求出1~N范围中所有的素数

    判断是否为素数 public static boolean isPrime int n int sqrt int Math sqrt n int i for i 2 i lt sqrt i if n i 0 break if i gt sq
  • 小程序瀑布流布局

    list wxml列表
  • Java的深浅拷贝机制

    Java的深浅拷贝机制 我们先理解一下深浅拷贝的概念 1 浅拷贝 Java在进行对象拷贝的过程中 对于他的成员变量进行拷贝 如果是基本数据类型 就会直接拷贝他的值 如果是引用类型 则会拷贝他的引用地址 而不会拷贝对象本身 2 深拷贝 Jav
  • Windows 10 和 Windows 11 有什么区别?

    Windows 10 和 Windows 11 有什么区别 Windows 11 具有 Windows 10 的全部功能和安全性 同时具有经重新设计而焕然一新的外观 它还自带一些新的工具 声音和应用 所有细节面面俱到 颜值 功能与安全性集于
  • Python字符串转为字典方法大全

    方法一 通过内置函数eval 1 2 3 4 5 6 7 8 9 10 11 12 13 str info name test age 18 dict info eval str info print string info type is
  • 【MYSQL】第一篇 Mysql如何处理百万级别的数据

    1 应尽量避免在 where子句中使用 或 lt gt 操作符 否则将引擎放弃使用索引而进行全表扫描 2 应尽量避免在 where子句中对字段进行 null值判断 否则将导致引擎放弃使用索引而进行全表扫描 3 尽量避免在 where子句中使
  • Jmeter性能测试 —— TPS拐点寻找

    寻找TPS性能拐点1 准备脚本 在本地电脑调试Jmeter压测脚本 上传到压测机Jmeter所在的服务器 2 执行压力测试 执行压测脚本 jmeter n t xianchengzuse jmx 记录业务压测数据 3 监控服务器性能指标 监
  • MySQL____高级查询、联合查询

    文章目录 一 聚合查询 1 count查询 总数统计 1 1count 用法1 1 2 count 用法2 1 3count 用法3 1 4 注意事项 2 SUM函数 总和统计 3 AVG函数 4 MAX函数 5 MIN函数 二 ifnul
  • 如何统计iOS产品不同渠道的下载量?

    一 前言 在开发过程中 Android可能会打出来很多的包 用于标识不同的商店下载量 原来觉得苹果只有一个商店 AppStore 如何做出不同来源的统计呢 本篇文章就是告诉大家如何做不同渠道来源统计 二 正文 先看一下苹果自家统计到的数据
  • unity局域网开关机步骤一-网络唤醒-设置电脑使允许局域网内其他电脑控制

    被控制端主机的设置 一 Bois界面网络唤醒的设置 外链图片转存失败 源站可能有防盗链机制 建议将图片保存下来直接上传 img TR2xwyAx 1593577482582 htts img blog csdnimg cn 20200701
  • 详细及易读懂的 大津法(OTSU)原理 和 比opencv自带更快的算法实现

    OTSU算法原理简述 最大类间方差是由日本学者大津 Nobuyuki Otsu 于1979年提出 是一种自适应的阈值确定方法 算法假设图像像素能够根据阈值 被分成背景 background 和目标 objects 两部分 然后 计算该最佳阈
  • C++之拷贝构造、拷贝赋值

    拷贝构造 class Stu public int no string name int age public Stu int no 10086 string name jin int age 18 no no name name age