11月29日笔记C++5_布尔类型 bool,字符串 string,引用,对象拷贝,右值引用,对象的移动

9 篇文章 0 订阅

1、布尔类型 bool 
    bool是C++的基础类型之一
    bool用于表示逻辑概念,只有两种值,即真和假
    在C++中,用true表示真,用false表示假
    bool的值占用一个字节的存储空间。

        (C++中不需要额外头文件,直接用<iostream>头文件)

    主要用于条件判断及函数返回类型。
    例: 
        判断一个链表是否为空
        bool isEmpty();
        bool ok = isEmpty();
        if (ok) // 判断条件为真
        {}

        if (!ok) // 判断条件为假
        {}


2、字符串 string 
    string是由标准C++库提供的一个类类型,用于表示字符串
    使用方式:
    1、包含头文件
    #include <string>
    2、实例化对象
        std::string 对象名; // 定义一个空的字符串
        std::string 对象名(初始值); // 定义字符串对象,同时初始化
        std::string 对象名 = 初始值; // 定义字符串对象,同时初始化

    3、使用公有方法
        size()/lenght()     用于计算字符串中实际字符的个数,不包括\0
        empty()               判断字符串是否为空
        at()                      返回给定索引位置的字符, 例:s1.at(0) 返回字符串的第一个字符
        c_str()                 把string类型的字符串 转换为 const char*
        substr()               获取字符串的子串
        同时支持以下运算符:
        =  +  +=  ==  !=   > >=  < <=    <<   >>   []                    
问题:
    当把一个复杂的对象以值的形式传递到一个函数中,有可能造成错误
    例: 

    StringList list1;
    list1.push_back("hello");

    print_list(list1); // void print_list(StringList list2);

    当调用该函数时,会在函数内部创建一个新的StringList类型的对象list2
    list2的成员变量的值与实参list1的成员变量的值一样。
    如果成员变量是指针,就会造成两个指针的值相同,即两个指针指向同一个对象(链表)

    而当函数调用完成,形参会自动销毁,对象销毁时会调用析构函数
    实参销毁时,也会调用析构函数

    也就是说同一个对象可能被释放两次!这是一种错误


引用
    引用是对象的别名!        
    语法格式:
        类型名 & 引用名 = 对象名;
    例: 

        int a = 10;
        int& r = a; // r是a的另一个名字,r就是a, a就是r,表示的是同一个对象

    特性:
        引用必须在定义的同时初始化
        引用一旦初始化完成,与它的初始对象就一直绑定在一起,不能再成为其它对象的别名。
        对引用进行的所有操作,都是在操作与之绑定的对象。
        引用的类型必须与它绑定的对象类型一致,但有以下例外
        1、允许常量引用绑定到非常量对象
            int a = 10;
            const int& r = a;
        2、基类引用可以绑定到派生类对象(下回分解)

    主要用于 函数传参 和 函数返回 。


    引用的分类
        常规引用
            绑定到左值的引用,也叫左值引用,如果希望通过引用修改绑定对象的值,则使用左值引用
        const引用
            既可以绑定到左值,也可以绑定到右值,如果不希望通过引用修改绑定对象的值,则使用const引用
        右值引用
            C++11中新增的特性,只能绑定到右值的引用,所引用的对象在使用之后,就无需要保留。 (下面详细介绍)
            右值引用主用于处理对象的移动。


对象拷贝
    拷贝构造
    拷贝赋值

拷贝构造函数
    用于拷贝对象的构造函数,称为 拷贝构造函数。
    拷贝构造函数名字与类名相同,但参数是本类类型的const引用
    例: 

    class
    {
    public:
        X(const X&); // 拷贝构造函数
    };

    如果类中没有显式的定义拷贝构造函数,编译器会自动生成。一般称为默认拷贝构造函数
    默认拷贝构造函数 执行对象的拷贝工作:
        内置类型的成员 直接拷贝
        类类型的成员,则调用成员对象的拷贝构造函数
        数组成员,则逐元素的拷贝数组中的成员。

    拷贝构造函数的调用时机
    1、用一个已存在的对象 初始化一个新对象
        例: 

        Point p1(100, 200);
        Point p2(p1); // Point p2 = p1;  Point p2{p1};

    2、一个对象作为 函数参数,以 值传递的方式传入函数

        void print(Point pos);
        print(p1); // Point pos = p1;

    3、一个对象作为 函数返回值,以值的方式从 返回 中返回

        Point func()
        {
            Point p3(0,0);
            return p3;
        }

    深拷贝与浅拷贝
        多数情况下,都可以使用 默认拷贝构造函数 来执行对象的拷贝
        但是,对于拥有资源的类来说,由默认拷贝构造函数执行的是浅拷贝(逐成员拷贝),则可能会出错误
        此时,需要自定义拷贝构造函数,以实现深层次的拷贝

        如果一个类拥有资源,当该类的对象发生拷贝时,资源重新分配,这种拷贝称为深拷贝
        反之,对象存在资源,但拷贝过程并 未拷贝 资源 的情况就称为浅拷贝

    省略拷贝构造
        1、在对象的初始化过程中,当 初始化表达式与  对象类型 为同一类型的 纯右值时
        例: 
            Point pos = Point(1, 2); // Point tmp(1,2); Point pos(tmp);
        2、在return语句中,操作的对象 与 函数返回类型 为同一类型的 纯右值时
            return Point(1,2);
    如果没有显式的定义拷贝构造函数,或定义了可在类的外部访问的拷贝构造函数

    阻止拷贝构造
        在C++11之前,常规做法,是把拷贝构造函数私有化(如果不显式的定义拷贝构造函数,编译器会自动生成一个公有的拷贝构造函数)
        例:

            class Screen
            {
            private:
                Screen(const Screen&); // 拷贝构造函数私有声明
            };

        在C++11中,引入了 删除函数 的概念
        即,如果不显式声明某些函数,编译器会自动生成,而有时候根本不需要编译器自动生成,此时,可以使用关键字 delete 显式的指示编译器删除某些自动生成的函数
        例: 

            class Screen
            {
            public:
                Screen(const Screen&) = delete; // 删除拷贝构造函数
            };

        注:
            如果需要某些默认生成的函数,可以使用关键字 default 显式的指示编译器生成相应的函数
        例: 

            class Point
            {
            public: 
                Point() = default; // 要编译器自动生成默认构造函数
                Point(int x, int y);
            };

        


右值引用
    常规引用 
    const引用
    右值引用
    所谓右值引用,就是必须绑定到右值的引用
    右值要么是字面常量,要么是在表达式求值过程中创建的临时对象
    通过&&来获取右值的引用。
    右值引用主要用来绑定到一个将要销毁的对象上,使用右值引用可以获取这个将要销毁的对象所拥有的资源。
    例: 

        int i = 10; // 变量是左值
        int& r = i; // OK 
        const int& r = i; // OK 

        int&& r = i+1;  // OK 
        int&& r = 11;   // OK 

        int&& r = i;    // error 右值引用不能绑定到左值


对象的移动
    移动构造
    移动赋值

    在很多情况下,都会发生对象的拷贝(如初始化、传参、返回等),在某些情况下,对象被拷贝后,就立即销毁了
    例: 

        Dir dir("./");
        StringList filenames = dir.getFileNames();
        getFileNames函数定义如下:
        StringList getFileNames()
        {
            StringList list;
            ...
            list.push_back(...);
            return list; // StringList tmp = list; list被拷贝后,立即销毁了
        }


        在这些情况下,如果不拷贝对象,而是把将要销毁的对象的资源 移动到 它们本来要拷贝到的地方去,则会大大的提升执行效率。

        为了支持移动操作,新标准引入了右值引用。
        移动操作的基本思想
            拷贝左值
            移动右值

        移动构造函数的参数是 非const 的右值引用
        除了完成资源的移动,移动构造函数 还必须确保 移动后 销毁源对象 是 安全的(不影响其它对象)
        而且一旦完成资源移动,源对象 必须不再指向 被移动的资源,即资源的所有权属于新对象了。

        例: 

            class ForwardList
            {
            public:
                ForwardList(ForwardList&& rhs)
                {
                    // 转移资源
                    this->head = rhs.head;
                    this->tail = rhs.tail;
                    this->size = rhs.size;
                    // 右侧对象放弃对资源的所有权
                    rhs.head = nullptr;
                    rhs.tail = nullptr;
                    rhs.size = 0;
                }
            private:
                Node* head;
                Node* tail;
                int size;
            };

            



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值