C++11新特性“=default”,“=delete”的使用

1、 =default 和=delete 概述

任何事物的出现都必然有着其出现的理由,伴随着每一个新的概念产生都会带来一系列的便利和价值。C++在不断的演变与发展,与此同时,伴随着许多新的特性和功能产生。=default、=delete 是C++11的新特性,分别为:显式缺省(告知编译器生成函数默认的缺省版本)和显式删除(告知编译器不生成函数默认的缺省版本)。C++11中引进这两种新特性的目的是为了增强对“类默认函数的控制”,从而让程序员更加精准地去控制默认版本的函数。其具体的功能和使用方法下面将一一道来。

2、 类与默认函数

在讲解关键字 default和delete 之前,先对类和类的默认函数作下描述与说明,从而加深对这两个关键字的理解与认知。既要知其然,也要知其所以然。C++中,当我们设计与编写一个类时,若不显著写明,则类会默认为我们提供如下几个函数:

(1)构造函数
(2)析构函数
(3)拷贝构造函数
(4)拷贝赋值函数(operator=)
(5)移动构造函数
以及全局的默认操作符函数:
(1)operator,
(2)operator &
(3)operator &&
(4)operator *
(5)operator->
(6)operator->*
(7)operator new
(8)operator delete

注:若我们在类中实现了这些版本之后,编译器便不会生成其对应的默认函数版本,这时需要我们显式的写上其对应的默认函数版本。

如例1所示:

/*************************************************************************
 * File Name: Student.cpp
 * Author:    The answer
 * Function:  Other
 * Mail:      2412799512@qq.com
 * Created Time: 2018年07月17日 星期二 23时08分20秒
 ************************************************************************/

#include<iostream>
using namespace std;
class Student
{
public:
    Student(const int a,const int b)
        :m_a(a)
        ,m_b(b)
    {

    }

int getA()const{return m_a;}
int getB()const{return m_b;}
private:
int m_a;
int m_b;
};

int main(int argc,char **argv)
{
Student stu(1,2);
cout<<stu.getA()<<endl; //1
cout<<stu.getB()<<endl; //2

    Student stu1;           //编译失败,报错: no matching function for call to ‘Student::Student()'

return 0;
}

编译方式:g++ Student.cpp

编译报错,提示:Student.cpp: In function ‘int main(int, char**)':
Student.cpp:34:13: error: no matching function for call to ‘Student::Student()'
Student stu1;

例1定义了一个对象stu1,该对象将会使用Student类的无参构造函数,而该默认构造函数在Student类中,我们没有显式的说明。因此,c++编译器在我们提供了该函数实现之后是不会生成与之对应的默认函数版本的。在Student中我们重载了带2个参数的构造函数,但是无参的构造函数,没有提供,因此会报错。

解决方式是:在该类中显式的提供无参构造函数,如下:

/*************************************************************************
 * File Name: Student.cpp
 * Author:    The answer
 * Function:  Other
 * Mail:      2412799512@qq.com
 * Created Time: 2018年07月17日 星期二 23时08分20秒
 ************************************************************************/

#include<iostream>
using namespace std;
class Student
{
public:
    Student(){}   //显式说明Student的无参构造函数
    Student(const int a,const int b)
        :m_a(a)
        ,m_b(b)
    {

    }

int getA()const{return m_a;}
int getB()const{return m_b;}
private:
int m_a;
int m_b;
};

int main(int argc,char **argv)
{
Student stu(1,2);
cout<<stu.getA()<<endl; //1
cout<<stu.getB()<<endl; //2

    Student stu1;
return 0;
}

问题:以 Student(){} 这样的方式来声明无参数构造函数,会带来一个问题,就是使得 其不再是 POD 类型,因此可能让编译器失去对这样的数据类型的优化功能。这是我们不希望看到的。因此最好使用 = default来修饰默认构造函数。

/*************************************************************************
 * File Name: Student.cpp
 * Author:    The answer
 * Function:  Other
 * Mail:      2412799512@qq.com
 * Created Time: 2018年07月17日 星期二 23时08分20秒
 ************************************************************************/

#include<iostream>
using namespace std;
class Student
{
public:
    Student() = default;
    Student(const int a,const int b)
        :m_a(a)
        ,m_b(b)
    {

    }

int getA()const{return m_a;}
int getB()const{return m_b;}
private:
int m_a;
int m_b;
};

int main(int argc,char **argv)
{
Student stu(1,2);
cout<<stu.getA()<<endl; //1
cout<<stu.getB()<<endl; //2

    Student stu1;

//使用is_pod模板类可以查看某类型是否属于POD类型,若为POD类型,则返回1,反之,返回0
std::cout<<is_pod<Student>::value<<std::endl;  //1
return 0;
}

更多关于is_pod的用法请参考: std::is_pod 。

3、 使用“=delete”来限制函数生成

C++开发中,我们经常需要控制某些函数的生成。在C++11之前,我们经常的普遍做法是将其声明为类的 private 成员函数,这样若在类外这些这些函数的操作时候,编译器便会报错,从而达到效果。如例2:

/*************************************************************************
 * File Name: Student.cpp
 * Author:    The answer
 * Function:  Other
 * Mail:      2412799512@qq.com
 * Created Time: 2018年07月17日 星期二 23时08分20秒
 ************************************************************************/

#include<iostream>
using namespace std;
class Student
{
public:
    Student() = default;
    Student(const int a,const int b)
        :m_a(a)
        ,m_b(b)
    {

    }

int getA()const{return m_a;}
int getB()const{return m_b;}

private:
    Student(const Student& );
    Student& operator =(const Student& );

private:
int m_a;
int m_b;
};

int main(int argc,char **argv)
{
Student stu(1,2);
cout<<stu.getA()<<endl; //1
cout<<stu.getB()<<endl; //2

//Student stu1(stu);
//报错:Student.cpp:26:5: error: ‘Student::Student(const Student&)' is private

//Student stu1(3,4);
//stu1 = stu;
//报错:Student.cpp:27:14: error: ‘Student& Student::operator=(const Student&)' is private

std::cout<<is_pod<Student>::value<<std::endl;  //
return 0;
}

例2代码编译报错,因为在类中,我们将Student的拷贝构造函数和拷贝赋值函数都声明为了 private 属性,因此,当在类外使用拷贝构造和拷贝赋值操作值,编译器会报错。虽然能够达到效果,但是不够直观和简洁。对于追求高效以及简洁来说,这样做有2个问题:

(1)不是最简化;
(2)对于友元支持不友好.

更为简洁直观的方法是使用:=delete

/************************************************************************
 * Author:    The answer
 * Function:  Other
 * Mail:      2412799512@qq.com
 * Created Time: 2018年07月17日 星期二 23时08分20秒
 ************************************************************************/

#include<iostream>
using namespace std;
class Student
{
public:
    Student() = default;
    Student(const int a,const int b)
        :m_a(a)
        ,m_b(b)
    {

    }

int getA()const{return m_a;}
int getB()const{return m_b;}

    Student(const Student& ) = delete;
    Student& operator =(const Student& ) = delete;

private:
int m_a;
int m_b;
};

int main(int argc,char **argv)
{
Student stu(1,2);
cout<<stu.getA()<<endl; //1
cout<<stu.getB()<<endl; //2

//Student stu1(stu);
//报错:Student.cpp:39:21: error: use of deleted function ‘Student::Student(const Student&)'

//Student(const Student& );

//Student stu1(3,4);
//stu1 = stu;
//报错:SStudent.cpp:44:10: error: use of deleted function ‘Student& Student::operator=(const Student&)'

std::cout<<is_pod<Student>::value<<std::endl;  //
return 0;
}

注:若缺省版本被删除了,重载该函数是非法的.

4、 “=default”使用范围

"=default"不仅仅局限于类的定义内,也可以用于类的定义外来修饰成员函数,如例3:
/*************************************************************************
 * File Name: Student.cpp
 * Author:    The answer
 * Function:  Other
 * Mail:      2412799512@qq.com
 * Created Time: 2018年07月17日 星期二 23时08分20秒
 ************************************************************************/

#include<iostream>
using namespace std;
class Student
{
public:
    Student() = default;
    Student(const int a,const int b)
        :m_a(a)
        ,m_b(b)
    {

    }

int getA()const{return m_a;}
int getB()const{return m_b;}

    Student(const Student& ) = delete;
    Student& operator=(const Student& );

private:
int m_a;
int m_b;
};

 Student& Student::operator =(const Student& ) = delete;

int main(int argc,char **argv)
{
Student stu(1,2);
cout<<stu.getA()<<endl; //1
cout<<stu.getB()<<endl; //2

Student stu1(3,4);
    stu1 = stu;
//编译报错:Student.cpp:42:10: error: use of deleted function ‘Student& Student::operator=(const Student&)'

std::cout<<is_pod<Student>::value<<std::endl;  //
return 0;
}

到此这篇关于C++11新特性“=default”,“=delete”的使用的文章就介绍到这了,更多相关C++11 =default =delete内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++11新特性之智能指针(shared_ptr/unique_ptr/weak_ptr)

    shared_ptr基本用法 shared_ptr采用引用计数的方式管理所指向的对象.当有一个新的shared_ptr指向同一个对象时(复制shared_ptr等),引用计数加1.当shared_ptr离开作用域时,引用计数减1.当引用计数为0时,释放所管理的内存. 这样做的好处在于解放了程序员手动释放内存的压力.之前,为了处理程序中的异常情况,往往需要将指针手动封装到类中,通过析构函数来释放动态分配的内存:现在这一过程就可以交给shared_ptr去做了. 一般我们使用make_shared来

  • 浅析C++11新特性的Lambda表达式

    lambda简介 熟悉Python的程序员应该对lambda不陌生.简单来说,lambda就是一个匿名的可调用代码块.在C++11新标准中,lambda具有如下格式: [capture list] (parameter list) -> return type { function body } 可以看到,他有四个组成部分: 1.capture list: 捕获列表 2.parameter list: 参数列表 3.return type: 返回类型 4.function body: 执行代码

  • C++11新特性std::make_tuple的使用

    std::tuple是C++ 11中引入的一个非常有用的结构,以前我们要返回一个包含不同数据类型的返回值,一般都需要自定义一个结构体或者通过函数的参数来返回,现在std::tuple就可以帮我们搞定. 1.引用头文件 #include <tuple> 2. Tuple初始化 std::tuple的初始化可以通过构造函数实现. // Creating and Initializing a tuple std::tuple<int, double, std::string> resul

  • C++11/14的新特性(更简洁)

    新的字符串表示方式--原生字符串(Raw String Literals) C/C++中提供了字符串,字符串的转义序列,给输出带来了很多不变,如果需要原生义的时候,需要反转义,比较麻烦. C++提供了,原生字符串,即字符串中无转义,亦无需再反义.详细规则见带码: #include <iostream> using namespace std; string path = "C:\Program Files (x86)\alipay\aliedit\5.1.0.3754";

  • 结合C++11的新特性来解析C++中的枚举与联合

    枚举 枚举是用户定义的类型,其中包含一组称为枚举器的命名的整型常数. 语法 // unscoped enum: enum [identifier] [: type] {enum-list}; // scoped enum: enum [class|struct] [identifier] [: type] {enum-list}; // Forward declaration of enumerations (C++11): enum A : int; // non-scoped enum mu

  • C++11新特性之auto的使用

    前言 C++是一种强类型语言,声明变量时必须明确指出其类型.但是,在实践中,优势我们很难推断出某个表达式的值的类型,尤其是随着模板类型的出现,要想弄明白某些复杂表达式的返回类型就变得更加困难.为了解决这个问题,C++11中引入的auto主要有两种用途:自动类型推断和返回值占位.auto在C++98中的标识临时变量的语义,由于使用极少且多余,在C++11中已被删除.前后两个标准的auto,完全是两个概念. 一.自动类型推断 auto自动类型推断,用于从初始化表达式中推断出变量的数据类型.通过aut

  • 结合C++11新特性来学习C++中lambda表达式的用法

    在 C++ 11 中,lambda 表达式(通常称为 "lambda")是一种在被调用的位置或作为参数传递给函数的位置定义匿名函数对象的简便方法. Lambda 通常用于封装传递给算法或异步方法的少量代码行. 本文定义了 lambda 是什么,将 lambda 与其他编程技术进行比较,描述其优点,并提供一个基本示例. Lambda 表达式的各部分 ISO C++ 标准展示了作为第三个参数传递给 std::sort() 函数的简单 lambda: #include <algorit

  • C++11新特性“=default”,“=delete”的使用

    1. =default 和=delete 概述 任何事物的出现都必然有着其出现的理由,伴随着每一个新的概念产生都会带来一系列的便利和价值.C++在不断的演变与发展,与此同时,伴随着许多新的特性和功能产生.=default.=delete 是C++11的新特性,分别为:显式缺省(告知编译器生成函数默认的缺省版本)和显式删除(告知编译器不生成函数默认的缺省版本).C++11中引进这两种新特性的目的是为了增强对"类默认函数的控制",从而让程序员更加精准地去控制默认版本的函数.其具体的功能和使

  • c++11新特性多线程操作实战

    c++11多线程操作 线程 thread int main() { thread t1(Test1); t1.join(); thread t2(Test2); t2.join(); thread t3 = t1; thread t4(t1); thread t5 = std::move(t1); thread t6(std::move(t1)); return 0; } t3,t4创建失败,因为thread的拷贝构造和赋值运算符重载的原型是: thread(const thread&) = d

  • c++11 新特性——智能指针使用详解

    c++11添加了新的智能指针,unique_ptr.shared_ptr和weak_ptr,同时也将auto_ptr置为废弃(deprecated). 但是在实际的使用过程中,很多人都会有这样的问题: 不知道三种智能指针的具体使用场景 无脑只使用shared_ptr 认为应该禁用raw pointer(裸指针,即Widget*这种形式),全部使用智能指针 初始化方法 class A { public: A(int size){ this->size = size; } A(){} void Sh

  • C++11新特性中auto 和 decltype 区别和联系

    C++11新特性中auto 和 decltype 区别和联系 一. auto简介 编程时候常常需要把表达式的值付给变量,需要在声明变量的时候清楚的知道变量是什么类型.然而做到这一点并非那么容易(特别是模板中),有时候根本做不到.为了解决这个问题,C++11新标准就引入了auto类型说明符,用它就能让编译器替我们去分析表达式所属的类型.和原来那些只对应某种特定的类型说明符(例如 int)不同.auto 让编译器通过初始值来进行类型推演.从而获得定义变量的类型,所以说 auto 定义的变量必须有初始

  • C++ 11新特性之大括号初始化详解

    本文主要给大家介绍了关于C++11新特性之大括号初始化的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍: C++11之前,C++主要有以下几种初始化方式: //小括号初始化 string str("hello"); //等号初始化 string str="hello"; //大括号初始化 struct Studnet{ char* name; int age; }; Studnet s={"dablelv",18}; //

  • 详解c++11新特性之模板的改进

    C++11关于模板有一些细节的改进: 模板的右尖括号 模板的别名 函数模板的默认模板参数 模板的右尖括号 C++11之前是不允许两个右尖括号出现的,会被认为是右移操作符,所以需要中间加个空格进行分割,避免发生编译错误. int main() { std::vector<std::vector<int>> a; // error std::vector<std::vector<int> > b; // ok } 这个我之前都不知道,我开始学编程的时候就已经是C

  • C++11新特性之自定义字面量

    1.示例 C++11新标准中引入了用户自定义字面量,也叫自定义后缀操作符,即通过实现一个后缀操作符,将申明了该后缀标识的字面量转化为需要的类型.考察如下代码: long double operator"" _mm(long double x) { return x / 1000; } long double operator"" _m(long double x) { return x; } long double operator"" _km(

  • C++11新特性std::tuple的使用方法

    1. 引入头文件 #include <tuple> 2. std::tuple初始化 std::tuple<int, std::string, float> t1(10, "Test", 3.14); 这里要注意,不是所有的C++ 11编译器都支持copy-list-initialization的方式.如下代码所示. std::tuple<int, int> foo_tuple() { return {1, -1}; // Error until N

随机推荐