#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++中的临时对象问题