如果地址不同,函数将释放str指向的内存,这是因为稍后将把一个新字符串的地址赋给str。如果不首先使用delete运算符,则上述字符串将保留在内存中。由于程序中不再包含指向该字符串的指针,因此这些内存被浪费掉。
参数传递模式每种方法都应清楚地指明参数的传递模式,参数可以按值、按引用或按指针传递。与const联合使用,参数会更加安全可靠。函数的原型用于向客户传达这些信息。
应尽可能的使用const。
在接下来的示例中,术语主调函数(caller)指的是g()函数(或者main程序),它调用另一个函数f()。在这种情况下,f()就是被调函数(callee),即g()所调用的函数。换言之,主调函数是发起转移控制权的函数,被调函数是接受控制权的函数。
1.void X::f(T arg) // 第一例,按值传递(pass by value)
被调函数可以对arg(原始对象的副本)进行读取和写入。
在f()内改动arg不会影响f()的主调函数,因为主调函数已提供原始对象的副本。这也许是参数传递最佳和最安全的模式,主调函数和被调函数互相保护。
但是按值传递在复制大型对象非常耗时。
2.void X::f(const T arg) // 第二例,按值传递
只读型按值传递参数,这种形式没人使用。
3.void X::f(T& arg) // 第一例,按引用传递(pass by reference)
这种形式意味着被调函数可对参数arg进行读写操作。
注意,arg属于主调函数,f()不会销毁它。
通常,在这种情况下,arg 是一个未初始化的对象,仅用于返回值(只是一个输出形参)。
4.void X::f(const T& arg) // 第二例,按引用传递
这种形式意味着被调函数对参数arg只具备读权利,不具备写权利。
在传递大型对象时,此传递样式为高效之道,强烈推荐使用。
5.void X::f(T argp)* // 第一例,按指针传递
这种形式在采用语义(adopt semantics)中很有用。它真正的含义是:主调函数将argp所指向的存储区(实际上是资源的生存期)的所有权职责传递给被调函数(即,属于f()的对象)。
主调函数创建了一个T类型的动态对象(可能使用new()),但是主调函数并不知道何时delete该动态对象(这种情况经常出现)。这是因为,被调函数可能仍然在使用它(或主调函数无法删除它),也可能是被调函数希望使用主调函数提供的存储区。在这种情况下,主调函数将argp所指向的对象的所有权职责移交给被调函数。换言之,被调函数采用argp指向的存储区。当被调函数不再需要argp所指向的对象时,要负责删除该对象。
6.void X::f(const T argp)* // 第二例,按指针传递。
这种形式意味着被调函数不能修改指针内容,但可以进行指针运算(++或--)。
7.void X::f(T const argp)*
这种形式意味着不能移动指针(即不允许对argp 进行运算)。
注意,你也可以删除 argp。虽然无法删除(编译时错误)指向const的指针,但const指针没有这样的限制。
8.void X::f(const T const argp)*
此例为(6)和(7)的组合。被调函数宣称它既不会修改argp所指向的内容,也不会对argp进行任何运算。这意味着,argp是一个只输入形参(in-only parameter)。
1.需要传递对象时,不要传递指针。
2.如果被调函数需要将真正的对象作为参数,则传递引用,而不是指针。
3.如果希望被调函数在对象中写入(输出形参),则传递引用,但不是对const的引用。
4.当传递基本类型参数时,要坚持使用值参数(value argument)。
尽量对参数和函数使用const限定符。前面介绍过,编译器能识别const限定符,这样做让编译器也参与其中,使得程序更加强健稳定。
函数返回值许多函数向主调函数返回值、引用或指针。要正确和高效地使用它们,必须先理解它们的含义。有以下几种模式返回:
T X::f(); // 按值返回T T* X::f(); // 返回T类对象的指针/地址 const T* X::f(); // 返回指向const T类对象的指针 T& X::f(); // 返回对T对象的引用 const T& X::f(); // 返回对const T类对象的引用1.绝不返回对局部变量的引用(或指向局部变量的指针)。一旦离开函数,局部变量将被销毁,但在此之后,引用(或指针)仍然存在。
2.如果在函数内部创建新对象,并且希望将该对象的所有权移交给主调函数,那么该函数必须返回一个指针。
当被调函数创建了一个新对象(或指向某对象的指针),但却不能控制该对象的生存期时,通常会出现这种情况。为这样的函数使用一种命名约定是个不错的想法(如CreateXXX())。