[Mac 10.7.1 Lion Intel-based x64 gcc4.2.1 xcode4.2]
Q: c语言中的const和c++中的const关键字的含义是一样的吗?
A: 不尽相同; c语言中const修饰的变量是不可更改的变量,它的核心还是变量;但是c++中const修饰的就是一个常量。比如,在c语言中使用const修饰的一个变量就不可以作为不可变长数组的元素个数,而c++就可以。
Q: const的修饰会影响重载函数吗?
A: 是的。
Q: 为什么下面的代码会提示函数重定义?
#include <iostream> using namespace std; int add(int a, int b) { return a + b; } int add(const int a, const int b) { return a + b; } int main (int argc, const char * argv[]) { return 0; }
Redefinition of 'add'
A: 这是因为const虽然对函数重载有影响,但是仅仅对于形参为引用或者指针类型才有效。因为const int a允许接收非const类型整形作为参数,那么这就和第一个add函数的形式出现了重复,到时候可能编译器也不知道程序员到底想调用哪个函数。
Q: 改成这样的形式就ok了。
int add(int a, int b) { return a + b; } int add(const int &a, const int &b) { return a + b; }
A: 这样确实不会编译出错,但是如果你去使用add函数,很容易就出现编译错误。
#include <iostream> using namespace std; #define COUT_ENDL(str) std::cout << #str << " is " << (str) << std::endl; int add(int a, int b) { return a + b; } int add(const int &a, const int &b) { return a + b; } int main (int argc, const char * argv[]) { COUT_ENDL(add(1, 2)) return 0; }编译提示:
Call to 'add' is ambiguous
Q: 难道就没有解决方法了吗?
A: 有的,可以将第一个add函数的形式改变一下。改变后如下:
int add(int &a, int &b) { return a + b; } int add(const int &a, const int &b) { return a + b; }
#include <iostream> using namespace std; #define COUT_ENDL(str) std::cout << #str << " is " << (str) << std::endl; #define COUT_CALL_FUNC(str) std::cout << "call func: " << __func__ << #str << std::endl; int add(int &a, int &b) { COUT_CALL_FUNC("add(int &a, int &b)") return a + b; } int add(const int &a, const int &b) { COUT_CALL_FUNC("add(const int &a, const int &b)") return a + b; } int main (int argc, const char * argv[]) { int i = 10, j = 20; COUT_ENDL(add(1, 2)) COUT_ENDL(add(i, j)) return 0; }
add(1, 2) is call func: add"add(const int &a, const int &b)" 3 add(i, j) is call func: add"add(int &a, int &b)" 30
Q: 既然const能体现出两个函数之间的区别,在类中用const修饰的函数和不用const修饰的函数是否也可以看成重载?
A: 是的。如下代码:
#include <iostream> using namespace std; #define COUT_ENDL(str) std::cout << #str << " is " << (str) << std::endl; #define COUT_CALL_FUNC(str) std::cout << "call func: " << __func__ << #str << std::endl; class Student { public: Student(int age):_age(age) { } ~Student(){ } public: int age() { return _age; } int age() const { return _age; } private: int _age; }; int main (int argc, const char * argv[]) { Student s(25); COUT_ENDL(s.age()) return 0; }保存为testForCpp.cpp, 编译生成testForCpp, 运行ok.
Q: 如何知道Student类中两个age函数是重载函数?
A: 为了保证符号表中可以看出两个age函数,将代码修改如下:
#include <iostream> using namespace std; #define COUT_ENDL(str) std::cout << #str << " is " << (str) << std::endl; #define COUT_CALL_FUNC(str) std::cout << "call func: " << __func__ << #str << std::endl; class Student { public: Student(int age):_age(age) { } ~Student(){ } public: int age() { return _age; } int age() const { return _age; } private: int _age; }; int main (int argc, const char * argv[]) { Student s(25); COUT_ENDL(s.age()) const Student s1(22); COUT_ENDL(s1.age()) return 0; }重新编译生成testForCpp.
使用nm命令得到可执行文件内部符号表包含age的:
可以看出,确实含有两个和age相关的函数,可以猜测即为代码中的两个age函数。为了确认,调试应用程序,在两个age函数入口加断点,运行:
在第一个age函数处中断,使用disassemble命令得到当前函数的汇编:
(gdb) disassemble Dump of assembler code for function _ZN7Student3ageEv: 0x0000000100000c70 <_ZN7Student3ageEv+0>: push %rbp 0x0000000100000c71 <_ZN7Student3ageEv+1>: mov %rsp,%rbp 0x0000000100000c74 <_ZN7Student3ageEv+4>: mov %rdi,-0x8(%rbp) 0x0000000100000c78 <_ZN7Student3ageEv+8>: mov -0x8(%rbp),%rdi 0x0000000100000c7c <_ZN7Student3ageEv+12>: mov (%rdi),%eax 0x0000000100000c7e <_ZN7Student3ageEv+14>: pop %rbp 0x0000000100000c7f <_ZN7Student3ageEv+15>: retq End of assembler dump. (gdb)
继续运行,到第二个age函数入口中断:
(gdb) disassemble Dump of assembler code for function _ZNK7Student3ageEv: 0x0000000100000c80 <_ZNK7Student3ageEv+0>: push %rbp 0x0000000100000c81 <_ZNK7Student3ageEv+1>: mov %rsp,%rbp 0x0000000100000c84 <_ZNK7Student3ageEv+4>: mov %rdi,-0x8(%rbp) 0x0000000100000c88 <_ZNK7Student3ageEv+8>: mov -0x8(%rbp),%rdi 0x0000000100000c8c <_ZNK7Student3ageEv+12>: mov (%rdi),%eax 0x0000000100000c8e <_ZNK7Student3ageEv+14>: pop %rbp 0x0000000100000c8f <_ZNK7Student3ageEv+15>: retq End of assembler dump. (gdb)同样可以确认它的函数名称。
Q: 对于类的拷贝构造函数,为什么形式参数一定得用const修饰,这个很有必要吗?
A: 是的。如下代码:
#include <iostream> using namespace std; #define COUT_ENDL(str) std::cout << #str << " is " << (str) << std::endl; class Student { public: Student(int age):_age(age) { } ~Student(){ } Student(Student &s); public: int age() { return _age; } int age() const { return _age; } private: int _age; }; Student::Student(Student &s) { _age = s._age; } int main (int argc, const char * argv[]) { const Student s1(21); Student s2(s1); COUT_ENDL(s2.age()) return 0; }
No matching constructor for initialization of 'Student'
总之,const关键字的加入打乱了原来的语言,语言必须为此付出新的代价,必须在某些时候为const考虑,成就c++体系的完整。
xichen
2012-5-31 16:18:43