[C/C++]_[中级]_[多态下引用和指针有什么区别]
2020-05-10 04:09
2641 查看
场景
-
在开发
C++
程序, 经常会用到多态引用和指针. 它们有什么区别吗? -
多态的引用和指针什么时候会调用虚函数表, 什么时候不会?
-
多态的引用和指针它们是调用的子类的方法还是父类的方法?
说明
引用变量
-
引用变量是
C++
相对C
增加的复合类型, 它是已定义变量
的别名,注意是已定义的变量, 必须是指向一个已定义的变量, 因此引用变量在声明时必须初始化。 -
引用类型使用
&
来声明引用, 类似于指针的*
声明为指针.比如以下的ii
就是一个引用变量.
int i; int& ii = i;
-
引用变量作为
已定义变量
的别名,对已定义变量
的任何操作都可以在引用变量里使用,比如取地址, 赋值等; 修改了引用变量等值也就是修改了已定义变量
的值。通常引用变量作为函数参数时,修改函数参数的值即修改了已定义变量的值。比如int& ii
.
多态
-
多态, 从字面上可以说明时多种行为形态。一般是指同一个方法,在子类和基类的的行为是不同的,即有继承。
-
引用或指针都能利用到多态的特性, 它们调用方法的行为取决于调用该方法的变量是什么类型或者实际对象是什么类型。
-
只有方法声明为虚函数时,引用和指针才会调用虚函数表.
-
当方法声明为虚函数时,引用和指针会调用实际对象的方法, 比如实际对象是子类, 调用引用变量是基类,那么会调用子类的虚函数。
-
而非虚函数的方法,根据调用的方法就是调用变量类型的方法,即使子类重载了基类的方法。比如调用变量是基类指针,指向的对象是子类对象,那么仍然调用的是基类的方法。
-
当我们写一个类的时候, 往往需要声明析构函数为虚方法, 这样做的目的是当子类析构时,会自动调用父类的析构函数。有些编译器会自动把每个类的析构函数加上
virtual
,析构时会自动调用父类的析构函数. 比如我这里的LLVM 5.1
.
引用和指针的区别
-
引用并不是一个地址, 而指针往往代表一个地址.
-
引用必须是一个已定义对象的别名,不能为
NULL
. -
类引用一般使用
.
作为调用属性方法的符号, 而指针需要使用->
. -
引用类型和指针类型可以使用
C++
的4种类型转换操作符相互转换.
例子
Brass.h
// // Brass.h // TestC++11 // // Created by sai on 8/23/19. // Copyright (c) 2019 sai. All rights reserved. // #ifndef __TestC__11__Brass__ #define __TestC__11__Brass__ #include <iostream> class Brass { public: void TestAdd(); void TestVirtualAdd(); virtual ~Brass(); }; #endif /* defined(__TestC__11__Brass__) */
Brass.cpp
// // Brass.cpp // TestC++11 // // Created by sai on 8/23/19. // Copyright (c) 2019 sai. All rights reserved. // #include "Brass.h" void Brass::TestAdd() { std::cout << "Brass::TestAdd" << std::endl; } Brass::~Brass() { std::cout << "Brass::~Brass: " << (int64_t)this << std::endl; } void Brass::TestVirtualAdd() { std::cout << "Brass::TestVirtualAdd" << std::endl; }
BrassPlus.h
// // BrassPlus.h // TestC++11 // // Created by sai on 8/23/19. // Copyright (c) 2019 sai. All rights reserved. // #ifndef __TestC__11__BrassPlus__ #define __TestC__11__BrassPlus__ #include <iostream> #include "Brass.h" class BrassPlus: public Brass { public: void TestAdd(); void TestVirtualAdd(); ~BrassPlus(); }; #endif /* defined(__TestC__11__BrassPlus__) */
BrassPlus.cpp
// // BrassPlus.cpp // TestC++11 // // Created by sai on 8/23/19. // Copyright (c) 2019 sai. All rights reserved. // #include "BrassPlus.h" void BrassPlus::TestAdd() { std::cout << "BrassPlus::TestAdd" << std::endl; } void BrassPlus::TestVirtualAdd() { std::cout << "BrassPlus::TestVirtualAdd" << std::endl; } BrassPlus::~BrassPlus() { std::cout << "BrassPlus::~BrassPlus: " << (int64_t)this << std::endl; }
BrassPlusPlus.h
// // BrassPlusPlus.h // TestC++11 // // Created by sai on 8/23/19. // Copyright (c) 2019 sai. All rights reserved. // #ifndef __TestC__11__BrassPlusPlus__ #define __TestC__11__BrassPlusPlus__ #include <iostream> #include "BrassPlus.h" class BrassPlusPlus: public BrassPlus { public: void TestAdd(); void TestVirtualAdd(); ~BrassPlusPlus(); }; #endif /* defined(__TestC__11__BrassPlusPlus__) */
BrassPlusPlus.cpp
// // BrassPlusPlus.cpp // TestC++11 // // Created by sai on 8/23/19. // Copyright (c) 2019 sai. All rights reserved. // #include "BrassPlusPlus.h" void BrassPlusPlus::TestAdd() { std::cout << "BrassPlus::TestAdd" << std::endl; } void BrassPlusPlus::TestVirtualAdd() { std::cout << "BrassPlus::TestVirtualAdd" << std::endl; } BrassPlusPlus::~BrassPlusPlus() { std::cout << "BrassPlusPlus::~BrassPlusPlus: " << (int64_t)this << std::endl; }
main.cpp
// // main.cpp // TestC++11 // // Created by sai on 11/15/16. // Copyright (c) 2016 sai. All rights reserved. // #include <iostream> #include <string> #include "Brass.h" #include "BrassPlusPlus.h" void TestPolymorphic() { Brass b; BrassPlusPlus bp; Brass& b1 = b; Brass& b2 = bp; BrassPlusPlus& bp1 = bp; std::cout << "Reference" << std::endl; b1.TestAdd(); b1.TestVirtualAdd(); b2.TestAdd(); b2.TestVirtualAdd(); bp1.TestAdd(); bp1.TestVirtualAdd(); std::cout << "Pointer" << std::endl; Brass* p1 = &b; Brass* p2 = &bp; BrassPlusPlus* bpp1 = &bp; void* bpp2 = bpp1; Brass& b3 = reinterpret_cast<Brass&>(*bpp2); p1->TestAdd(); p1->TestVirtualAdd(); p2->TestAdd(); p2->TestVirtualAdd(); bpp1->TestAdd(); bpp1->TestVirtualAdd(); b3.TestAdd(); b3.TestVirtualAdd(); } int main(int argc, const char * argv[]) { TestPolymorphic(); return 0; }
输出
Reference Brass::TestAdd Brass::TestVirtualAdd Brass::TestAdd Brass::TestVirtualAdd BrassPlus::TestAdd BrassPlus::TestVirtualAdd Pointer Brass::TestAdd Brass::TestVirtualAdd Brass::TestAdd Brass::TestVirtualAdd BrassPlus::TestAdd BrassPlus::TestVirtualAdd Brass::TestAdd Brass::TestVirtualAdd BrassPlusPlus::~BrassPlusPlus: 3221223744 BrassPlus::~BrassPlus: 3221223744 Brass::~Brass: 3221223744 Brass::~Brass: 3221223752 Program ended with exit code: 0
参考
15.5 类型转换操作符 << C++ Primer Plus >> 第五版
8.2 引用变量 << C++ Primer Plus >> 第五版
13.4 多态公有继承. << C++ Primer Plus >> 第五版
infoworld 博客专家 原创文章 347获赞 126访问量 120万+ 关注 他的留言板相关文章推荐
- C++ 中指针和引用有什么区别详解
- C++中指针和引用有什么区别?
- java的引用和C++的指针有什么区别
- C++ 中指针和引用的什么区别
- C++小知识:引用和指针的区别是什么?
- C++中引用与指针有什么区别
- More Effective C++----(1)指针与引用的区别 & (2)尽量使用C++风格的类型转换 & (3)不要对数组使用多态
- C++ 中指针和引用的什么区别
- Java的引用和C++的指针有什么区别?
- java的引用和c++的指针有什么区别
- 引用与指针区别C++
- [收集] C++ 指针与引用的区别
- C++中引用与指针的区别(详细介绍)
- [转]C++中引用和指针的区别
- c++中,引用和指针的区别
- 详解C++中指针(*)、取地址(&)、解引用(*)与引用(&)的区别 (完整代码)
- 浅谈C++中指针和引用的区别
- c++引用与指针的区别
- 【补足基础】C++中引用传递与指针传递区别【转】
- C++中指针和引用的区别