Effective C++读书笔记----自定义类型的传参和返回值问题

  • 对于自定义类型,传参的时候尽可能的使用传引用来代替传值。

看如下这个例子:

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

class Person
{
public:
    Person()//默认的构造函数,如果不给,没法通过编译,因为在创建一个派生类的对象是需要调用。
    {
        cout << "Person()" << endl;
    }

    Person(const Person& p)//拷贝构造函数
    {
        cout << "Person(const Person& )" << endl;
    }

    virtual ~Person()//析构函数
    {
        cout << "~Person()" << endl;
    }
private:
    string name;
    string address;
};

class Student: public Person
{
public:
    Student()//默认的构造函数
    {
        cout << "Student()" << endl;
    }
    Student(const Student& s)//拷贝构造函数
    {
        cout << "Student(const Student& )" << endl;
    }
    ~Student()//析构函数
    {
        cout << "~Student()" << endl;
    }
private:
    string Schoolname;
    string Schooladdress;
};


void test(Student stu)//用于测试传值过程中调用的构造析构函数的次数
{
    cout << endl << "function test is running" << endl << endl;
}

int main()
{
    Student s;
    test(s);//进行传值调用测试函数
    return 0;
}

在如上代码中定义了Person和Student两个类。类Student公有继承自Person。他们分别有两个string类对象成员。为了方便测试调用构造函数和析构函数的次数,在构造函数和拷贝构造函数中都打印出了对应的而标志。
执行结果:
这里写图片描述
我们真正需要创建的就一个对象,调用一次构造一次析构就可以了。但是,在通过值传递调用测试函数的时候又调用了构造函数。而且,我们还没有讨论两个类的成员。基类个派生类的分别有两个string类类型的成员。成员也是类类型,那么,在创建对象的时候势必也要调用类成员的构造函数。
基类和派生类各自有两个类成员,也就是说在创建一个派生类对象,需要调用基类的构造函数、类成员的构造函数、以及它自身的构造函数。而在本例中,调用基类的构造函数构建基类部分的时候还要调用基类成员的构造函数。在根据实参拷贝出一个实参的过程中,调用了基类构造函数一次,基类的两个成员变量分别调用构造函数,调用了3次构造函数才将基类部分构建出来,构建派生类的部分又调用了3次构造函数。总共就是调用6次构造函数和6次析构函数。这开销还是挺大的,而且很浪费时间。
结果中没有显示出为类成员调用的构造函数和析构函数,因为string类的构造函数和析构函数不是我们实现的,没有相关的调用成功的标志。
如果使用传const引用来替代传值,效率会高很多。因为不需要调用那系列的构造函数,而且使用const加以修饰,也可保证在调用函数中不会对他进行修改操作。除此之外,还有一个益处:防止对象被切割。如果传递的是一个派生类类型,而函数的参数是一个基类类型,那么,传进去的这个参数会被当做一个基类对象,它派生类的那部分会被“切割”掉。

  • 必须返回对象时,不要妄想返回引用。

直接返回一个对象要进行额外的构造和析构,但是有时候这也是在所难免的。因为,想要返回一个引用,那么,首先得有一个已经存在了的对象,然后返回这个对象的引用。
如果这个对象是通过参数传进来的,那么没什么问题。
如果这个对象是自己新创建的,如果是一个在栈上开辟的局部变量,那么这就是返回了栈空间的引用,相当于返回了栈空间的地址,这是非法的。
如果这个对象是用new在堆上开辟的,将它返回没有没什么毛病,但是,在堆上构建这个对象时需要调用拷贝构造函数,这跟直接返回对象调用构造函数也没什么区别只是时间早晚的问题。而且,这还有一个潜在的危险,那就是需要调用者来管理这块在堆上开辟的空间,调用者可能忘记释放这块空间,也可能对这块空间释放多次。并且,这个函数只要调用一次就要为new一次。
可能会有人想到说把它设置为静态变量,但是设置为静态变量的话又引入了线程安全的问题。

猜你喜欢

转载自blog.csdn.net/guaiguaihenguai/article/details/81430304