const、右值引用、临时对象、析构函数的执行时机

#include <iostream>

using namespace std;

class Student
{
private:
    string _name;

public:
    Student( const char * name) { _name = name; }   //= Student( string name) { _name = name; }
    ~Student() { cout<<"Destructor"<<endl; }   //析构函数;

    void print(const Student &stu){   //某些时候= void print( Student &stu),因为可以将普通的对象绑定
                                      //到const引用,这儿之所以加const,是为了照顾stu5;
        cout<<"带const参数的print:"<<stu._name<<endl;
    }

    void print( Student &stu){   //其实在本程序中= void print(const Student &stu),因为可以将普通的对象
                                 //绑定到const引用,并且函数内部只是读取stu,并不需要更改stu的值;
        cout<<"带参数(非const)的print:"<<stu._name<<endl;
    }
    void print(){
        cout<<"不带参数的print:"<<this->_name<<endl;
    }
    friend void print(const Student &stu); //声明为友元函数(只需要在普通声明的基础上加friend关键字。
                                           //之所以声明为友元函数,是因为函数是在类外定义的,并且要
};                                         //使用类内的private成员函数;

void print(const Student &stu){   //友元函数的定义(也称为实现);
    cout<<"带参数的print:"<<stu._name<<endl;
}

int main()
{
    Student && stu1 = Student("Randy"); //创建无名对象再调用拷贝构造函数初始化"右值引用";
                          //因为创建临时对象返回的是右值,所以必须用右值引用来绑定;
    cout<<"stu1:"<<endl;
    stu1.print();
    stu1.print(stu1);
    cout<<endl;

    Student stu2("Randy");//= Student stu2 = "Randy";第二种方式涉及到类型的默认转换;
                          //可以认为是string类型通过调用构造函数往Student类型的转换;
                          //但是通常创建对象我们都是使用第一种方式;
    cout<<"stu2:"<<endl;
    stu2.print();
    stu2.print(stu2);
    cout<<endl;

    Student stu3 = Student("Randy"); //此时相当于Student stu3(Randy)来调用构造函数;见[1]
    cout<<"stu3:"<<endl;
    stu3.print();
    stu3.print(stu2);
    cout<<endl;

  /*const Student & stu4_0 = Student("Randy"); //编译无法通过;此时const引用有丢失数据的风险,编译器不允许这么做;
    cout<<"stu4_1:";                           //解决方法就是使用stu1那种方式
    stu4.print();

    const Student & stu4_1 = Student("Randy"); //编译无法通过;原因同上;
    cout<<"stu4_1:";
    stu4_1.print(stu4_1);  */

    cout<<"stu5:"<<endl;
    print(Student("Randy"));  //使用临时对象作为函数实参(这时返回的是一个右值),形参一定要
    cout<<endl;               //加const;因为左值引用不能绑定到返回右值的表达式,见CPPP-P471;

    cout<<"end of main"<<endl;

    return 0;
}

/*通过分别注释stu*的各段函数,发现stu1、stu2、stu3在程序的结束分别调用一次析构函数;所以在end of main语句之
 *后输出三个Destructor;
 *在所有函数之外创建的对象是全局对象,它和全局变量类似,位于内存分区中的全局数据区,程序在结束执行时会调用
 *这些对象的析构函数;
 *在函数内部创建的对象是局部对象,它和局部变量类似,位于栈区,函数执行结束时会调用这些对象的析构函数;
 * new 创建的对象位于堆区,通过 delete 删除时才会调用析构函数;如果没有 delete,析构函数就不会被执行; [2]
 */

输出为:




参考:

[1] C++中的临时对象:这儿对于临时对象做一下总结,有三种情况会产生临时对象:

  • 一是 对象做函数参数,进行传值调用,会产生临时对象;
  • 二是 直接建立一个未命名的临时对象(我认为是最常用的一种,也最直接的一种);
  • 三是 函数的返回值是一个对象,此时会在栈中产生一个临时对象,存储函数的返回值(这儿说一下关于返回值的问题,子函数中的都是局部变量,函数执行结束变量也就销毁了,那么return的值是怎么实现的呢?首先要对return的值进行拷贝,拷贝到的位置肯定不是子函数的栈区,因为子函数的栈区在函数执行结束就销毁了。实际上可以拷贝到寄存器中,也可以拷贝到内存中,依赖于编译器的实现。

[2] 析构函数的执行时机:c语言编程网

[3] C++ Primer P471介绍了右值引用的使用;P46介绍了普通引用(左值引用)的基本用法;P55介绍了const引用的使用;P534介绍了基类引用可以绑定到派生类的引用;P121介绍了右值和左值的区别,在一般的情况下,左值不能用右值代替,但是右值可以用左值代替;

[4] 关于C++中的临时对象问题

[5] C++中无名对象的作用(程序参考)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值