高质量程序设计指南c++/c语言(17)--函数
对于静态链接库的函数库或者类库,如果你调用了其中的函数,那么连接器会从相应的库中提取这些函数的实现代码并把他们连接到你的程序中,如果你没有调用库中的某些函数,则连接器是不会把他们的实现代码连接进来的,即使该库包含了成千上万个函数。如果你使用的是动态链接库,则运行时必须将所有dll都复制到运行环境的相应目录下。
连接的本质就是把一个名字的实现代码绑定到对它的每一个引用语句上,如果你根本没有引用这个名字,那么它的实现和谁去绑定呢?
同样,如果程序中任何地方都没有调用你自己编写的某个函数的话,编译器也不会为该函数生成可执行代码。明白了这个道理,你应该可以消除一些不必要的担心了。例如,我们在开发一些通用类的时候,应该设计并实现完整的功能,而不需要去担心它的使用者会因为仅使用其中一小部分功能却要包含整个类定义而导致代码体积增大。
1、 函数的最完整原型如下:
[作用域] [函数的连接规范] 返回值类型 [函数的调用规范] 函数名 (参数列表);
2、不要把返回指针的函数用作左值
double *fun(double *p) { return p; } double d=100.0; *fun(&d) = 200.0;
虽然编译器可以接受,但是最后这个语句非常难以理解,应该避免类似的写法。
3、return语句不可返回指向堆栈内存的指针或者引用
char *fun(void) //error { char str[] = "hello world"; cout << sizeof(str) << endl; //12 cout << strlen(str) << endl; //11 return str; } const char *fun(void) //ok { const char *p = "hello world"; //字符串常量存储于静态数据区 cout << sizeof(str) << endl; //4 cout << strlen(str) << endl; //11 return str; }
4、如果函数返回值是一个对象,要考虑return 语句的效率
例如:
class String { public: String() { cout << "String()" << endl; } String(const String &string) { cout << "String(const String &string)" << endl; } String & operator=(const String &string) { cout << "String & operator=(const String &string)" << endl; return *this; } ~String() { cout << "~String()" << endl; } };
版本一:
String test(const String &s) { String result(s); //String(const String &string) return result; //返回的时候用result初始化main作用域内的一个临时对象,调用String(const String &string),并发生resut的析构,调用~String() } int main(void) { String s1; //String() String s2; //String() s2 = test(s1); //将临时对象(不是test函数内的result)赋值给s2,调用String & operator=(const String &string),这条语句执行完毕后,临时对象的声明周期结束,故调用~String() return 0; } //main函数结束时,发生s1和s2的析构
版本二:
String test(const String &s) { return String(s); } int main(void) { String s1; String s2; s2 = test(s1); return 0; }
输出如下:
String()
String()
String(const String &string)
String & operator=(const String &string)
~String()
~String()
~String()
Press any key to continue
test函数内的return String(s);表示创建一个临时对象并返回它。编译器直接把临时对象创建并初始化在外部存储单元中,省去了拷贝和析构的开销,并提高了效率。