书上和网上在很多地方都对const 的重载做了一些解释,但感觉都不是很详细。还有很多同学在不同的地方发问关于const 重载的问题,这里我又重新看了一下,做了一个简单的分析也可能有不对的地方,欢迎讨论。
所谓重载,是指允许存在多个同名函数,而这些函数的参数表不同,即函数名相同但函数的签名不同。重载并不是面向对象编程的特有属性,这是因为重载是在编译阶段实现的,编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(这一点稍后有例子)。了
Const 参数重载解析:
关于const 重载几乎在所有c++的书中者提到过但大部分只是一句话,例如在《C++ primer》一书中这样描述:“可基于函数的引用形参是指向 const 对象还是指向非 const 对象,实现函数重载。将引用形参定义为 const 来重载函数是合法的,因为编译器可以根据实参是否为 const 确定调用哪一个函数。”
但是这一段描述并没有给出引用、指针和值传递前加const的实质区别是什么。在用非const的指针,引用和值均可转化为const的。这一点没有太多可说明的东东。
对于函数值传递的情况,因为参数传递是通过复制实参创建一个临时变量传递进函数的,函数内只能改变临时变量,但无法改变实参。则这个时候无论加不加const对实参不会产生任何影响。但是在引用或指针传递函数调用中,因为传进去的是一个引用或指针,这样函数内部可以改变引用或指针所指向的变量,这时const 才是实实在在地保护了实参所指向的变量。因为在编译阶段编译器对调用函数的选择是根据实参进行的,所以,只有引用传递和指针传递可以用是否加const来重载。
下面给出一个例子可能就更明白了:
- C++ 代码
- #include<iostream>
- class A{
- public:
- A();
- int foo(int *test);
- int foo(const int *test);
- };
- A::A(){
- }
- int A::foo(int *test){
- std::cout << *test << " A::foo(int *test)" <<std::endl;
- return 1;
- }
- int A::foo(const int *test){
- std::cout << *test << " A::foo(const int *test)" <<std::endl;
- return 1;
- }
- int main()
- {
- const int b =5;
- int c = 3;
- A a;
- a.foo(&b);
- a.foo(&c);
- return 1;
- }
输出:
- A::foo(const int *test)
- A::foo(int *test)
那么编译器又是怎样工作的,通过g++ -S选项将汇编代码生成出来,通过AT&T汇编代码可以看出一些端倪来(之所以用AT&T汇编是因为VS生成的中间代码实在是让人头晕):
- 代码
- .file "overload.cpp"
- .section .ctors,"aw",@progbits
- .align 4
- .long _GLOBAL__I__ZN1AC2Ev
- .text
- .align 2
- .globl _ZN1AC2Ev
- .type _ZN1AC2Ev, @function
- _ZN1AC2Ev:
- .LFB1399:
- pushl %ebp
- .LCFI0:
- movl %esp, %ebp
- .LCFI1:
- popl %ebp
- ret
- .LFE1399:
- .size _ZN1AC2Ev, .-_ZN1AC2Ev
- .globl __gxx_personality_v0
- .align 2
- .globl _ZN1AC1Ev
- .type _ZN1AC1Ev, @function
- _ZN1AC1Ev:
- .LFB1400:
- pushl %ebp
- .LCFI2:
- movl %esp, %ebp
- .LCFI3:
- popl %ebp
- ret
- .LFE1400:
- .size _ZN1AC1Ev, .-_ZN1AC1Ev
- .align 2
- .type _Z41__static_initialization_and_destruction_0ii, @function
- _Z41__static_initialization_and_destruction_0ii:
- .LFB1411:
- pushl %ebp
- .LCFI4:
- movl %esp, %ebp
- .LCFI5:
- subl $24, %esp
- .LCFI6:
- movl %eax, -4(%ebp)
- movl %edx, -8(%ebp)
- cmpl $1, -4(%ebp)
- jne .L9
- cmpl $65535, -8(%ebp)
- jne .L9
- movl $_ZSt8__ioinit, (%esp)
- call _ZNSt8ios_base4InitC1Ev
- movl $__dso_handle, 8(%esp)
- movl $0, 4(%esp)
- movl $__tcf_0, (%esp)
- call __cxa_atexit
- .L9:
- leave
- ret
- .LFE1411:
- .size _Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
- .align 2
- .type _GLOBAL__I__ZN1AC2Ev, @function
- _GLOBAL__I__ZN1AC2Ev:
- .LFB1413:
- pushl %ebp
- .LCFI7:
- movl %esp, %ebp
- .LCFI8:
- subl $8, %esp
- .LCFI9:
- movl $65535, %edx
- movl $1, %eax
- call _Z41__static_initialization_and_destruction_0ii
- leave
- ret
- .LFE1413:
- .size _GLOBAL__I__ZN1AC2Ev, .-_GLOBAL__I__ZN1AC2Ev
- .align 2
- .type __tcf_0, @function
- __tcf_0:
- .LFB1412:
- pushl %ebp
- .LCFI10:
- movl %esp, %ebp
- .LCFI11:
- subl $8, %esp
- .LCFI12:
- movl $_ZSt8__ioinit, (%esp)
- call _ZNSt8ios_base4InitD1Ev
- leave
- ret
- .LFE1412:
- .size __tcf_0, .-__tcf_0
- .section .rodata
- .LC0:
- .string " A::foo(const int *test)"
- .text
- .align 2
- .globl _ZN1A3fooEPKi
- .type _ZN1A3fooEPKi, @function
- _ZN1A3fooEPKi:
- .LFB1402:
- pushl %ebp
- .LCFI13:
- movl %esp, %ebp
- .LCFI14:
- subl $8, %esp
- .LCFI15:
- movl 12(%ebp), %eax
- movl (%eax), %eax
- movl %eax, 4(%esp)
- movl $_ZSt4cout, (%esp)
- call _ZNSolsEi
- movl $.LC0, 4(%esp)
- movl %eax, (%esp)
- call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
- movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, 4(%esp)
- movl %eax, (%esp)
- call _ZNSolsEPFRSoS_E
- movl $1, %eax
- leave
- ret
- .LFE1402:
- .size _ZN1A3fooEPKi, .-_ZN1A3fooEPKi
- .section .rodata
- .LC1:
- .string " A::foo(int *test)"
- .text
- .align 2
- .globl _ZN1A3fooEPi
- .type _ZN1A3fooEPi, @function
- _ZN1A3fooEPi:
- .LFB1401:
- pushl %ebp
- .LCFI16:
- movl %esp, %ebp
- .LCFI17:
- subl $8, %esp
- .LCFI18:
- movl 12(%ebp), %eax
- movl (%eax), %eax
- movl %eax, 4(%esp)
- movl $_ZSt4cout, (%esp)
- call _ZNSolsEi
- movl $.LC1, 4(%esp)
- movl %eax, (%esp)
- call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
- movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, 4(%esp)
- movl %eax, (%esp)
- call _ZNSolsEPFRSoS_E
- movl $1, %eax
- leave
- ret
- .LFE1401:
- .size _ZN1A3fooEPi, .-_ZN1A3fooEPi
- .align 2
- .globl main
- .type main, @function
- main:
- .LFB1403:
- leal 4(%esp), %ecx
- .LCFI19:
- andl $-16, %esp
- pushl -4(%ecx)
- .LCFI20:
- pushl %ebp
- .LCFI21:
- movl %esp, %ebp
- .LCFI22:
- pushl %ecx
- .LCFI23:
- subl $36, %esp
- .LCFI24:
- movl $5, -8(%ebp)
- movl $3, -12(%ebp)
- leal -13(%ebp), %eax
- movl %eax, (%esp)
- call _ZN1AC1Ev
- leal -8(%ebp), %eax
- movl %eax, 4(%esp)
- leal -13(%ebp), %eax
- movl %eax, (%esp)
- call _ZN1A3fooEPKi
- leal -12(%ebp), %eax
- movl %eax, 4(%esp)
- leal -13(%ebp), %eax
- movl %eax, (%esp)
- call _ZN1A3fooEPi
- movl $1, %eax
- addl $36, %esp
- popl %ecx
- popl %ebp
- leal -4(%ecx), %esp
- ret
- .LFE1403:
- .size main, .-main
- .local _ZSt8__ioinit
- .comm _ZSt8__ioinit,1,1
- .weakref _Z20__gthrw_pthread_oncePiPFvvE,pthread_once
- .weakref _Z27__gthrw_pthread_getspecificj,pthread_getspecific
- .weakref _Z27__gthrw_pthread_setspecificjPKv,pthread_setspecific
- .weakref _Z22__gthrw_pthread_createPmPK14pthread_attr_tPFPvS3_ES3_,pthread_create
- .weakref _Z22__gthrw_pthread_cancelm,pthread_cancel
- .weakref _Z26__gthrw_pthread_mutex_lockP15pthread_mutex_t,pthread_mutex_lock
- .weakref _Z29__gthrw_pthread_mutex_trylockP15pthread_mutex_t,pthread_mutex_trylock
- .weakref _Z28__gthrw_pthread_mutex_unlockP15pthread_mutex_t,pthread_mutex_unlock
- .weakref _Z26__gthrw_pthread_mutex_initP15pthread_mutex_tPK19pthread_mutexattr_t,pthread_mutex_init
- .weakref _Z26__gthrw_pthread_key_createPjPFvPvE,pthread_key_create
- .weakref _Z26__gthrw_pthread_key_deletej,pthread_key_delete
- .weakref _Z30__gthrw_pthread_mutexattr_initP19pthread_mutexattr_t,pthread_mutexattr_init
- .weakref _Z33__gthrw_pthread_mutexattr_settypeP19pthread_mutexattr_ti,pthread_mutexattr_settype
- .weakref _Z33__gthrw_pthread_mutexattr_destroyP19pthread_mutexattr_t,pthread_mutexattr_destroy
- .section .eh_frame,"a",@progbits
- .Lframe1:
- .long .LECIE1-.LSCIE1
- .LSCIE1:
- .long 0x0
- .byte 0x1
- .string "zP"
- .uleb128 0x1
- .sleb128 -4
- .byte 0x8
- .uleb128 0x5
- .byte 0x0
- .long __gxx_personality_v0
- .byte 0xc
- .uleb128 0x4
- .uleb128 0x4
- .byte 0x88
- .uleb128 0x1
- .align 4
- .LECIE1:
- .LSFDE5:
- .long .LEFDE5-.LASFDE5
- .LASFDE5:
- .long .LASFDE5-.Lframe1
- .long .LFB1411
- .long .LFE1411-.LFB1411
- .uleb128 0x0
- .byte 0x4
- .long .LCFI4-.LFB1411
- .byte 0xe
- .uleb128 0x8
- .byte 0x85
- .uleb128 0x2
- .byte 0x4
- .long .LCFI5-.LCFI4
- .byte 0xd
- .uleb128 0x5
- .align 4
- .LEFDE5:
- .LSFDE7:
- .long .LEFDE7-.LASFDE7
- .LASFDE7:
- .long .LASFDE7-.Lframe1
- .long .LFB1413
- .long .LFE1413-.LFB1413
- .uleb128 0x0
- .byte 0x4
- .long .LCFI7-.LFB1413
- .byte 0xe
- .uleb128 0x8
- .byte 0x85
- .uleb128 0x2
- .byte 0x4
- .long .LCFI8-.LCFI7
- .byte 0xd
- .uleb128 0x5
- .align 4
- .LEFDE7:
- .LSFDE9:
- .long .LEFDE9-.LASFDE9
- .LASFDE9:
- .long .LASFDE9-.Lframe1
- .long .LFB1412
- .long .LFE1412-.LFB1412
- .uleb128 0x0
- .byte 0x4
- .long .LCFI10-.LFB1412
- .byte 0xe
- .uleb128 0x8
- .byte 0x85
- .uleb128 0x2
- .byte 0x4
- .long .LCFI11-.LCFI10
- .byte 0xd
- .uleb128 0x5
- .align 4
- .LEFDE9:
- .LSFDE11:
- .long .LEFDE11-.LASFDE11
- .LASFDE11:
- .long .LASFDE11-.Lframe1
- .long .LFB1402
- .long .LFE1402-.LFB1402
- .uleb128 0x0
- .byte 0x4
- .long .LCFI13-.LFB1402
- .byte 0xe
- .uleb128 0x8
- .byte 0x85
- .uleb128 0x2
- .byte 0x4
- .long .LCFI14-.LCFI13
- .byte 0xd
- .uleb128 0x5
- .align 4
- .LEFDE11:
- .LSFDE13:
- .long .LEFDE13-.LASFDE13
- .LASFDE13:
- .long .LASFDE13-.Lframe1
- .long .LFB1401
- .long .LFE1401-.LFB1401
- .uleb128 0x0
- .byte 0x4
- .long .LCFI16-.LFB1401
- .byte 0xe
- .uleb128 0x8
- .byte 0x85
- .uleb128 0x2
- .byte 0x4
- .long .LCFI17-.LCFI16
- .byte 0xd
- .uleb128 0x5
- .align 4
- .LEFDE13:
- .LSFDE15:
- .long .LEFDE15-.LASFDE15
- .LASFDE15:
- .long .LASFDE15-.Lframe1
- .long .LFB1403
- .long .LFE1403-.LFB1403
- .uleb128 0x0
- .byte 0x4
- .long .LCFI19-.LFB1403
- .byte 0xc
- .uleb128 0x1
- .uleb128 0x0
- .byte 0x9
- .uleb128 0x4
- .uleb128 0x1
- .byte 0x4
- .long .LCFI20-.LCFI19
- .byte 0xc
- .uleb128 0x4
- .uleb128 0x4
- .byte 0x4
- .long .LCFI21-.LCFI20
- .byte 0xe
- .uleb128 0x8
- .byte 0x85
- .uleb128 0x2
- .byte 0x4
- .long .LCFI22-.LCFI21
- .byte 0xd
- .uleb128 0x5
- .byte 0x4
- .long .LCFI23-.LCFI22
- .byte 0x84
- .uleb128 0x3
- .align 4
- .LEFDE15:
- .ident "GCC: (GNU) 4.1.2 20070925 (Red Hat 4.1.2-33)"
- .section .note.GNU-stack,"",@progbits
如上面的代码函数:
int foo(int *test);和int foo(const int *test);分别被编译器生成名为:_ZN1A3fooEPKi和_ZN1A3fooEPi(这两个名字会因为编译器的不同而不同,名字只是一个区分的符号而已不用深究,只用知道重载的函数经过编译器的处理函数名字已经发生了变化。所以对于后面的汇编和链接工作就不存在重载的问题了。)这里也同时说明对重载来说在编译阶段已经完成。
对于a.foo(&b);因为变量b有const修饰所以就调用了int foo(const int *test);对于a.foo(&c);调用int foo(int *test);因为这个是精确匹配的。但是如果没有定义int foo(const int *test);则在代码24行会出现编译错误。反过来如果没有定义函数:int foo(int *test);如下:
- 代码
- #include<iostream>
- class A{
- public:
- A();
- // int foo(int *test);
- int foo(const int *test);
- };
- A::A(){
- }
- /*int A::foo(int *test){
- std::cout << *test << " A::foo(int *test)" <<std::endl;
- return 1;
- }
- */
- int A::foo(const int *test){
- std::cout << *test << " A::foo(const int *test)" <<std::endl;
- return 1;
- }
- int main()
- {
- const int b =5;
- int c = 3;
- A a;
- a.foo(&b);
- a.foo(&c);
- return 1;
- }
则输出结果为:
- 5 A::foo(const int *test)
- 3 A::foo(const int *test)
原因c++ primer上讲的很清楚:“We can use a nonconst object to initializer either a const or nonconst reference. However, initializing a constreference to a nonconst object requires a conversion, whereas initializing a nonconst parameter is an exact match.”
const 成员函数重载的解析:
const 成员函数重载的解析和const参数重载解析的原理可以说是一样的。之所以这样说是因为const成员函数的解析可被看做是对函数this参数用const来修饰的过程。例如下面代码:
- 代码
- #include<iostream>
- class A{
- public:
- A();
- int foo(int *test); //可看做:int foo(A *this,int *test);
- int foo(int *test) const;//可看做:int foo(const A *this,int *test);
- };
- A::A(){
- }
- int A::foo(int *test){
- std::cout << *test << "foo" <<std::endl;
- return 1;
- }
- int A::foo(int *test) const {
- std::cout << *test << "foo const" <<std::endl;
- return 1;
- }
- int main()
- {
- int b = 5;
- const A a;
- a.foo(&b);
- return 1;
- }
上面可以看到编译阶段的调用也是通过对重载函数的别名来实现的。
总结:
1.const重载主要是通过能否对传入的参数进行修改为判断的。http://blog.csdn.net/net_assassin/article/details/9997257
2.const参数重载和const函数重载机制都是一样的,因为对于const 函数重载可看做是对隐含的指针this的参数重载。
3.重载是在编译阶段已经完成,对于汇编和链接来说透明的。
FROM:http://blog.csdn.net/net_assassin/article/details/9997257