C++11:Default和Delete

【C++11保姆级教程】delete和default关键字 欢迎来到本教程,今天我们将学习C++11中的两个关键字:delete和default。这两个关键字为我们提供了更清晰和更灵活的方式来控制类的默认行为。无论您是初学者还是有经验的开发人员,本教程都将以通俗易懂的语言解释这两个关键字的概念和用法。让我们开始吧!在C++11中,delete关键字可以用于删除类的特殊成员函数或禁止使用特定的函数。通过使用delete关键字,我们可以明确地告诉编译器某个函数是禁止的,从而防止使用者无意中调用它们。注意:是类中的成员函数或禁止使用特定的函数,不要放错了地方。 阅读详情

目录

 

引子

默认构造函数

析构函数

默认拷贝构造函数

默认拷贝赋值运算

默认移动构造函数

默认移动赋值函数

default

delete

C++11之前

C++11实现


引子

default和delete是C++11新添加的关键字,依靠这两个关键字C++编译器可以控制函数的默认生成和删除,这是对C++98标准的很大升级。这儿需要说明的是default仅仅可以控制类的特殊成员函数的默认生成,而delete可应用于任何函数。

为了可更好的说明C++11新引入的default和delete特性,我们需要对类的特殊成员函数加以描述和说明,所谓类的特殊成员函数就是编译器隐式为类自动生成的成员函数。C++98标准类的特殊成员函数4个,他们分别是:默认构造函数,默认拷贝构造函数,默认拷贝赋值运算,析构函数。而C++11标准类的特殊成员函数在这4个基础上又扩展了2个,他们是移动构造函数,移动赋值运算。

默认构造函数

如果你定义了一个类没有实现任何构造函数,那么编译器会默认帮你添加一个构造函数,此构造函数就是类的默认构造函数;默认构造函数不接受任何输入参数,也无任何返回值。

默认构造函数并不是一个空函数,默认构造函数会完成类成员变量的默认初始化工作,但是类成员变量默认初始化为何值,除C++标准中有明确说明默认初始实现限制的成员变量以外,其他成员变量C++标准没有特定说明,不同的编译器有不同的实现。所以类的默认构造函数有时会给我们未定义行为。如果一个类包含一个指针成员变量,那么在默认构造时此指针的默认值就是一个未定义行为,编译器厂商可以默认初始此指针为null指针或一个无意义的野指针。

假设定义一个类CUser,那么编译器生成的默认构造函数可以用下面的代码描述:

class CUser 
{
public:
    CUser () 
    {
        // 1. m_pszName指针的初始化,可能会null,也有可能是其他也指针
        // 2. m_nSex 默认初始化,一般情况下m_nSex会初始为0
    }
private:
    char* m_pszName;  // 名称
    int   m_nSex;     // 性别
};

析构函数

析构函数与类的构造函数相对应,当一个对象释放时,类的析构函数一定会被调用。所以如果我们实现了一个类类型,没有实现此类类型析构函数,那么编译器同样会默认帮我们添加一个默认析构函数,否则在类对象释放时,就会报出析构函数不存在错误了。

同默认构造函数一样,默认析构函数同样不是一个空函数,它会实现类成员放操作。成员对象的释放操作按照成员的类型可分为指针成员和非指针成员。对于指针成员,默认析构仅仅释放指针所占用的内存开销;而对于非指针成员,析构函数会调用其析构函数完成对象析构。

假设定义一个类CUser,那么编译器生成的默认析构函数可以用下面的代码描述:

class CUser 
{
    public:~ CUser () 
    {
        // 1. 指针成员,释放指针所占用的内存开销    
        // 2. 成员对象,调用其析构函数完成对象释放
    }
    
    private:
        char* m_pszName;  // 名称
        int   m_nSex;     // 性别
};

默认拷贝构造函数

拷贝构造函数实现由一个存在的对象采用内存拷贝方式构建一个不存在的对象,请牢记这一点构建的对象原本是不存在的;这个很重要,因为它是默认构造和默认赋值的唯一区别,也决定了两个默认函数何时会被调用。

由上面的描述,我们可得出这样的一个结论:默认拷贝构造函数存在输入参数,而且输出是一个已经存在的同类型对象,而默认拷贝构造函数的功能则是用“老对象”的数据成员对“新对象”的数据成员进行一对一赋值,即所谓的浅拷贝(关于浅拷贝后续会有专门主题详细论述)。

假设定义一个CUser类型,那么编译默认生成的拷贝构造函数,可用下面代码描述:

class CUser
{
public:
     // 缺省的支持浅拷贝的拷贝构造函数
     CUser (const CUser & other) : m_pszName (other.m_pszName) , m_nSex(other. m_nSex) {}
 
private:
    char* m_pszName;  // 名称
    int   m_nSex;     // 性别
};
 
CUser  liming;   // 调用默认构造函数
CUser  lining = liming;  // 调用拷贝构造函数

默认拷贝赋值运算

拷贝赋值运算实现用一个已经存在的对象通过内存拷贝的方式去修改另外一个已经存在的同类型对象。拷贝赋值由赋值运算符“=”触发,当用 = 运算符定义变量时调用拷贝构造函数,而对于已经存在的变量使用 = 则是拷贝赋值运算,这是需要和拷贝构造函数需要区分的。

由上面的描述,我们可得出这样的一个结论:默认拷贝赋值函数存在输入参数,而且输出是一个已经存在的同类型对象,而默认拷贝赋值函数的功能则是用赋值对象的数据成员对被赋值对象的数据成员进行一对一赋值,而不会对被赋值对象成员进行特殊释放操作,也是一种浅拷贝操作。

假设定义一个CUser类型,那么编译默认生成的拷贝赋值函数,可用下面代码描述,默认拷贝构造的输入参数为const类型左值引用。

class CUser
{
public:
    // 缺省的支持浅拷贝的拷贝赋值函数
    CUser&  operator=  (const CUser & other) 
    {
        m_pszName  = other.m_pszName; 
        m_nSex = other. m_nSex;
    }
 
private:
    char* m_pszName;  // 名称
    int   m_nSex;     // 性别
};
 
CUser  liming;    // 调用默认构造函数
CUser  lining;    // 调用默认构造函数
lining = liming;    // 调用拷贝赋值函数

默认移动构造函数

默认构造函数实现由一个已存在的左值对象复制并创建另外一个左值对象,而移动构造函数在不同,移动构造不会发生复制操作,而是通过交换成员的拥有者实现一个左值对象构建。但不论默认构造还是移动构造,他们的目标却是一样的,同是创建一个左值对象。构造函数拷贝构造函数输入参数为const类型左值引用,所以触发拷贝构造函数时,输入必须为一个左值类型的对象。那么默认移动构造函数呢?默认移动构造函数输入参数为右值引用,而接受的输入参数必须为一个右值或一个将亡值。

为了更好的说明移动构造的作用,我们先思考一下下面这个例子发生了什么

class CUser
{
public:
    CUser() {}
 
private:
   char* m_pszName;  // 名称
    int   m_nSex;     // 性别
};
 
CUser  CreateUser() 
{
   CUser user;
    return user
}
 
CUser  lining = CreateUser();

有过C++开发经验的同学应该都会明白,上面这段代码第14行函数返回时编译器将产生一个临时对象,并调用拷贝构造函数将user对象复制到临时变量包括user对象中可能存在大内存资源对象。所以可以看出简简单单的一个函数返回竟然产生了这么多内存开销,那么有没有改进实现呢?移动构造函数是C++11新引入的标准,目的是为了解决此临时对象赋值操作时的内存开销(关于移动构造后续会有专门主题详细论述)。

那么针对于CUser类类型,C++11 编译器默认生成的默认拷贝构造函数,可以用下面的代码展示,输入参数为一个非const类型的右值引用。

class CUser 
{
public:
CUser (CUser&&  other) 
{
    m_nSex = other. m_nSex;
    m_pszName = other. m_pszName;
    other. m_pszName = nullptr;
    other. m_nSex = 0;
}
private:
    char* m_pszName;  // 名称
    int   m_nSex;     // 性别
};
 
CUser user = CUser(); // 匿名对象/临时对象优先调用右值引用

移动构造和拷贝构造对比

  1. 移动构造和拷贝构造都是对象的构造函数,都可以完成对象的创建工作
  2. 移动构造将一个将右值或亡值的资源直接接管了过来,无需再去申请新的内存。尤其是对象中所含堆上资源比较大的情况下,在效率上的体现是非常高的。
  3. 拷贝构造将一个左值的资源通过拷贝的方式复制,需要重新申请内存,尤其是对象中所含堆上资源比较大的情况下,在效率上的体现是很低的。

默认移动赋值函数

移动赋值实现将一个将亡值的资源直接接管,而省去昂贵的内存开销,移动赋值的输入参数和默认移动构造一样是一个右值引用。一般情况下,如果用一个即将构造的对象给一个已存在的对象赋值,会触发移动赋值函数的调用。例如:

CUser user();
user = CUser();

移动构造函数的声明格式如下:CUser&  operator=  (const CUser && other)

移动赋值和拷贝赋值对比

  1. 移动赋值将一个将右值或亡值的资源直接接管了过来,无需再去申请新的内存。尤其是对象中所含堆上资源比较大的情况下,在效率上是非常高的。
  2. 拷贝赋值将一个左值的资源通过拷贝的方式复制,需要重新申请内存,尤其是对象中所含堆上资源比较大的情况下,在效率上是很低的。

default

C++11引入的default关键字,功能是指示编译器完成类特殊成员函数的默认生成工作,程序员只需在类特殊成员函数后面加上“=default;”,即可将该函数声明为 "=default"函数,编译器将为显式声明 "=default"函数自动生成默认函数体。除此之外,"=default"还具有如下两个优势:(1) 可以获取更高的执行效率,(2) 避免了程序员定义函数的繁重工作。

default函数可在类体内inline定义,也可以在类体外out-line定义,但是需要特别说明的是:default只可应用于类的6个特殊成员函数,其他成员函数不能使用=default,例如:

class CUser {
public:
    CUser () = default;    // inline定义
    CUser (const CUser &); 
    CUser (CUser &&) = default; // inline定义
    CUser & operator = (const CUser&);
    CUser & operator = (CUser&&) = default; // inline定义
    virtual ~ CUser () = default;  // inline 定义
    int get() = default; // default 不能作用一般函数
};
 
CUser:: CUser (const CUser &) = default;  // out of line定义
CUser & CUser::operator = (const CUser &) = default; // out of line定义

C++98标准虽然没有default关键字,但是一般情况下编译器会默认帮程序员添加类的默认构造,析构函数,拷贝构造函数以及拷贝赋值函数。C++11的default只是把这个默认实现变的更直观而已。

delete

delete关键字主要是控制类的6个特殊函数的使用工作,声明为“=default”的函数在其他地方无法使用,即这个函数不存在,禁止定义。这是C++11为了程序员的使用便利,而添加的新特性,当然,在C++11之前程序员也可以禁止4个特殊函数定义,控制函数的使用,只是实现方式有所不同。

所以本节将会分为两个小节,第一小节笔者将说明C++11之前的实现方式,而后第二小节笔者将详细说明C++11 delete关键字的使用。

C++11之前

在C++11标准之前,程序员想控制一个函数不允许被定义,一般通过两个手段达到目标:(1)声明此函数为私有成员函数;(2)此函数只有函数声明,没有函数定义。例如:

class CUser {
public:
    CUser (const CUser &); // 声明类的拷贝构造函数
    CUser & operator = (const CUser&);// 声明拷贝赋值函数
    virtual ~ CUser () {}
private:
    CUser ();    // 声明类的默认构造函数,没有函数定义体
};
 
CUser::CUser(const CUser &) {
}
 
CUser & CUser::operator = (const CUser&) {
}
 
CUser user; // 编译器会报错

C++11实现

程序员只需在函数声明后加上“=delete;”,就可将该函数声明为 "=delete"函数,声明为"=delete"的函数,编译器将明确禁止此函数的定义。需要特别说明的是delete可以用于任何函数不限制于类的特殊成员函数。例如下例中GetUser函数和ReleaseAt函数。

class CUserMgr {
public:
    CUserMgr (const CUserMgr &) = delete; // 禁止拷贝构造函数 
    CUserMgr (CUserMgr &&) = delete; 
    CUserMgr & operator = (const CUserMgr &) = delete;
    CUserMgr & operator = (CUserMgr &&) = delete; 
    virtual ~ CUserMgr () = default;
    void ReleaseAt(nIndex) = delete; //“=delete”特性可以作用于非特殊成员函数
    static CUserMgr& Instance();
private:
    CUserMgr () = default;
};
CUserMgr& CUserMgr::Instance() {
    static CUserMgr userMgr;
    return userMgr
}
 
CUser*  GetUser(int nIndex) = delete;  // “=delete”特性可以作用于非成员函数
C++(11):显示删除函数=delete 【代码】C++(11):显示删除函数=delete。 阅读详情

相关推荐

C++中的default和delete关键字用法

在C++中,default和delete是两个特殊的关键字,用于控制类的特殊成员函数的生成和禁止。本文将详细介绍default和delete关键字的用法,并提供相应的源代码示例。default关键字用于指示编译器生成默认的特殊成员函数,而delete关键字用于显式禁用类的特殊成员函数或普通成员函数。在上面的示例中,我们使用"delete"关键字声明了禁用的复制构造函数和赋值运算符。通过使用delete关键字,我们可以明确地表达某些函数不应该被使用,从而避免潜在的错误或不必要的行为。

HackQuestR的博客 222

创建的函数有编译错误_C++11 中的 Defaulted 函数

对于 defaulted 函数,编译器会为其自动生成默认的函数定义体,从而获得更高的代码执行效率,也可免除程序员手动定义该函数的工作量。Defaulted 函数背景问题C++ 的类有四类特殊成员函数,它们分别是:默认构造函数、析构函数、拷贝构造函数以及拷贝赋值运算符。这些类的特殊成员函数负责创建、初始化、销毁,或者拷贝类的对象。如果程序员没有显式地为一个类定义某个特殊成员函数,而又需要用到该特殊成...

weixin_35992880的博客 486

[C++11 类的改进] --- 继承控制:=default和=delete

在 C++ 中声明自定义的类,编译器会默认帮助程序员生成一些他们未自定义的成员函数。这样的函数版本被称为” 默认函数”。这样的函数一共有六个:有时程序员会忘记上面提到的规则,最常见的是声明了带参数的构造,如果还需要无参构造函数,这时候必须定义出不带参数的版本。不过通过编译器的提示,这样的问题通常会得到更正。但更为严重的问题是,一旦声明了自定义版本的构造函数,则有可能导致我们定义的类型不再是 POD 类型,我们便不再能够享受 POD 类型为我们带来的便利。对于上面提到的这些,我们无需过度担心,因为 C++11

M2嵌入式 624

c++类对象的初始化笔记(1)

c++ 构造函数的学习,记录

weixin_42108533的博客 765

特性阻抗 – SI/PI 世界碰撞的地方

除了谐振零点和峰值外,在一定频率下,传输线的阻抗看起来像远端开路时的电容器的阻抗,也看起来像远端短路时电感的阻抗。如果随后的制造变化将测得的阻抗推到 -10% 容差的低端(例如 44 欧姆)的测量区域内,那么如果在初始下降时测量,则真正的特性阻抗将在 42 欧姆时降低 5%,并且超出规格。这意味着,当我们测量红色或蓝色图的峰和相邻谷之间的中间阻抗时,它是该频率下传输线的特性阻抗。图 7 远端开路时(红色常亮)与远端短路时(蓝色常亮)的电感和电容抗阻抗与频率的关系(上图)和传输线的阻抗与频率(下图)的对比。

jkh920184196的博客 962

DaVinciCFG实战:从ARXML配置到ADC代码生成全解析

本文详细解析了使用DaVinciCFG工具进行ADC模块配置的全过程,从ARXML文件处理到代码生成与集成。重点介绍了关键参数配置、ARXML语法规则、常见错误排查及代码优化技巧,帮助开发者掌握AUTOSAR架构下ADC驱动开发,提升汽车电子系统的可靠性。

f9g0h的博客 378

C++11 新特性:default 和 delete

C++11 中引入的default和delete关键字,为类设计提供了更大的灵活性和控制力。通过显式地声明类的特殊成员函数为默认或删除,开发者可以更精确地指定类的行为,使得类的设计意图更加明确,同时也提高了代码的可读性和维护性。

kelvin_yin的专栏 692

VSS shielding线可以floating吗?

shielding vss不打孔,就相当于dummy metal,如果周围有其他干扰线,他们影响了这段dummy metal,这段dummy metal仍然会对目标线产生干扰,bug的是,工具知道这段dummy是vss,那么计算寄生的时候仍然把它当vss,就有可能噪声没有完全消除,而用户又看不见。而真正接地的线,相当于一直是0,就不会对目标线产生干扰,一般加shield是在route之前,这时候shield能尽量覆盖目标线,等到绕线后再加就会出现shield只覆盖到一部分目标线的情况。

m0_61544122的博客 6633

【转】c++11 类默认函数的控制:"=default" 和 "=delete"函数

c++11 类默认函数的控制:"=default" 和 "=delete"函数 转自:https://www.cnblogs.com/lsgxeva/p/7787438.html #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #include <vector&...

Keep Fighting! 188

STM32F103/F407/H743不同GPIO速度配置(HAL库)对应的最高速度

详细介绍了STM32F103/F407/H743不同GPIO速度配置(HAL库)对应的最高速度。

kevin1499的博客 5587

petalinux2022.2启动文件编译配置

petalinux2021之前的版本里,导入目录components/yocto/下的“environment-setup-aarch64-xilinx-linux” 和 “layers/core/oe-init-build-env”的环境,能使用Yocto的devtool工具。(加入下面内容,先petalinux-build下载完成后才能有源码)制作 BOOT.BIN 启动文件:petalinux-package --boot --fsbl --fpga --u-boot --force。

wangjie36的博客 1386

【C/C++】C++11新特性:=default 和 =delete

参考自:微软文档显式默认设置的函数和已删除的函数 C++11 标准新特性:Defaulted 和 Deleted 函数 C++11 中值得关注的几大变化(详解) C++11以前,我们习惯将拷贝构造函数和拷贝赋值函数设置为 private 来禁止类的拷贝行为。 class noncopyable { public: noncopyable() {};

mick_seu的博客 1966

cannot import name ‘BUFSIZE‘ from ‘numpy‘ 解决方案

本文主要介绍了cannot import name ‘BUFSIZE’ from ‘numpy’ 解决方案,希望能对使用numpy的同学们有所帮助。 文章目录 1. 问题描述 2. 解决方案

weixin_43178406的博客 22万+

深入理解C++11: 03noncopyable(=default和=delete函数)

深入理解C++11中的=default =delete函数

Erice_s的博客 1337

48、住宅抵押贷款支持证券的定价、风险评估与结构化设计

本文深入探讨了住宅抵押贷款支持证券(RMBS)的定价、风险评估与结构化设计。内容涵盖提高定价准确性的方差缩减方法,如准蒙特卡罗序列的应用;分析RMBS面临的利率风险、信用风险和模型风险,并提出相应的度量与应对策略;介绍利用动态规划优化CMO分层结构以最大化发行利润的方法。同时总结了RMBS的基本类型、定价流程及市场发展趋势,为投资者和发行人提供全面的决策支持。

quiet的专栏 154

基于Simulink进行步进电机的重复控制仿真建模

重复控制概述目的:通过引入一个周期延迟环节来补偿系统的周期性误差,从而提高系统对周期性参考信号或扰动的跟踪性能。方法内部模型原理:在控制器中嵌入一个与周期性信号相关的模型(例如周期延迟环节),以实现对周期性误差的零稳态误差控制。滤波器设计:为了避免高频噪声放大,通常会在重复控制器中加入低通滤波器。优势对周期性信号具有良好的跟踪性能。不需要精确的数学模型即可实现高性能控制。挑战:需要合理设计重复控制器的参数以避免系统不稳定。

amy_mhd的博客 266

C++11新特性:default和delete

解释 类默认函数的控制 程序员只需在函数声明后加上“=default;”,就可将该函数声明为 "=default"函数,编译器将为显式声明的 "=default"函数自动生成函数体,因为必须要有一个构造函数有函数体。 程序员只需在函数声明后上“=delete;”,就可将该函数禁用。 =default 声明函数为默认构造函数 =delete 禁用该函数 样例 #ifdef _CV_H #define _CV_H class cv{ cv(const cv&) = delete; cv&

Jiewang的博客 561

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

c++11 类默认函数的控制:"=default" 和 "=delete"函数

原文https://www.cnblogs.com/lsgxeva/category/1107731.html #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #include <vector> #include <map> //c++...

weixin_30951389的博客 95

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

c++11标准 类默认函数的控制:“=default“ 和 “=delete“函数

C++ 的类有四类特殊成员函数,它们分别是:默认构造函数、析构函数、拷贝构造函数以及拷贝赋值运算符。 这些类的特殊成员函数负责创建、初始化、销毁,或者拷贝类的对象。 如果程序员没有显式地为一个类定义某个特殊成员函数,而又需要用到该特殊成员函数时,则编译器会隐式的为这个类生成一个默认的特殊成员函数。 C++11 标准引入了一个新特性:"=default"函数。程序员只需在函数声明后加上“=defau...

流楚丶格念的博客 4511

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

C++11 标准新特性:Defaulted 和 Deleted 函数 =default, =delete

C++11 标准新特性:Defaulted 和 Deleted 函数 =default, =delete

weixin_37958272的博客 162
上一篇: C++前置++和后置++的区别
下一篇: std::condition_variable.wait()的用法和设计缺陷带来的坑
杂质想当杂志
博客等级 码龄10年 13粉丝 32原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值