IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++11/14新特性--=default、=delete、tuple -> 正文阅读

[C++知识库]C++11/14新特性--=default、=delete、tuple


1、概要

=default=delete显式缺省(告知编译器生成函数默认的缺省版本)显式删除(告知编译器不生成函数默认的缺省版本)

C++11中引进这两种新特性的目的是为了增强对“类默认函数的控制”,从而让程序员更加精准地去控制默认版本的函数。


2、“=default ”类与默认函数

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

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

示例:

#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;
} 

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

#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来修饰默认构造函数。

#include<iostream>
using namespace std;
class Student
{
   public:
    Student() = default;  //使用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;
}


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

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

#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;
}

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

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

#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"不仅仅局限于类的定义内,也可以用于类的定义外来修饰成员函数。

#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;
}


5、tuple

tuple容器(元组), 是表示元组容器, 是不包含任何结构的,快速而低质(粗制滥造, quick and dirty)的, 可以用于函数返回多个返回值;

tuple容器, 可以使用直接初始化, 和"make_tuple()"初始化, 访问元素使用"get<>()"方法, 注意get里面的位置信息, 必须是常量表达式(const expression);

可以通过"std::tuple_size<decltype(t)>::value"获取元素数量; "std::tuple_element<0, decltype(t)>::type"获取元素类型;

如果tuple类型进行比较, 则需要保持元素数量相同, 类型可以比较, 如相同类型, 或可以相互转换类型(int&double);

无法通过普通的方法遍历tuple容器, 因为"get<>()"方法, 无法使用变量获取值;

以下代码包含一些基本的用法, 详见注释:

#include <iostream>  
#include <vector>  
#include <string>  
#include <tuple>  
using namespace std;  

std::tuple<std::string, int>  
giveName(void)  
{  
    std::string cw("Caroline");  
    int a(2019);  
    std::tuple<std::string, int> t = std::make_tuple(cw, a);  
    return t;  
}  

int main()  
{  
    std::tuple<int, double, std::string> t(64, 128.0, "Caroline");  
    std::tuple<std::string, std::string, int> t2 =  
            std::make_tuple("Caroline", "Wendy", 1992);  


    //返回元素个数  
    size_t num = std::tuple_size<decltype(t)>::value;  
    std::cout << "num = " << num << std::endl;  

    //获取第1个值的元素类型  
    std::tuple_element<1, decltype(t)>::type cnt = std::get<1>(t);  
    std::cout << "cnt = " << cnt << std::endl;  

    //比较  
    std::tuple<int, int> ti(24, 48);  
    std::tuple<double, double> td(28.0, 56.0);  
    bool b = (ti < td);  
    std::cout << "b = " << b << std::endl;  

    //tuple作为返回值  
    auto a = giveName();  
    std::cout << "name: " << get<0>(a)  
            << " years: " << get<1>(a) << std::endl;  

    return 0;  

}  

运行结果:
num = 3
cnt = 128
b = 1
name:Caroline
years: 2019

在C++中的tuple和python语言中是类似的,是一个强大的允许存放多个不同类型数据的容器,是对pair的泛化。
要在C++中使用tuple,首先需要引用头文件tuple及名空间std。


tuple相关函数

1)make_tuple: 用于创建tuple

auto tup1 = std::make_tuple("Hello World!", 'a', 3.14, 0);  

上述代码创建了一个tuple <const char*, char, double, int>类型的元组。
可以看出,在tuple之中可以是完全不同的数据类型。


2) tie: 用于拆开tuple

auto tup1 = std::make_tuple(3.14, 1, 'a');  
double a;  
int b;  
char c;  
std::tie(a, b, c) = tup1;  

这样做的结果是a = 3.14, b = 1, c = ‘a’。

如果不想要某一位的值,可以直接将其用ignore代替。

 std::tie(ignore, b, c) = tup1;  


3)forward_as_tuple: 用于接受右值引用数据生成tuple

auto tup2 = std::forward_as_tuple(1, "hello");  

上述代码创建了一个tuple<int &&, char (&)[6]>类型的元组。
可以看出,tuple中的参数全部为右值引用。而前面讨论的tie函数就只能接受左值。


4) tuple_cat: 用于连接tuple

std::tuple<float, string> tup1(3.14, "pi");  
std::tuple<int, char> tup2(10, 'a');  
auto tup3 = tuple_cat(tup1, tup2);  

将tup1和tup2连起来就成了tup3。


5) get< i > : 获取第 i 个元素的值

std::tuple<float, string> tup1(3.14, "pi");  
cout << get<0>(tup1);  

这样就输出了tup1中的第一个元素3.14.


6) tuple_element: 获取tuple中特定元素数据类型

std::tuple_element<0, decltype(tup1)>::type  

这样就获取到了tup1中第一个元素的数据类型。
注意:获取到的就是数据类型,如int,char。而不是写有“int”或者“char”的字符串。


7)size: 获取tuple中元素个数

std::tuple<float, string> tup1(3.14, "pi");  
cout << tuple_size<decltype(tup1)>::value;  

输出结果为2,表示该tuple中有两个元素。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-07-22 13:57:27  更:2021-07-22 13:58:32 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/1 23:41:52-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码