您的位置:首页 > 编程语言 > C语言/C++

[C/C++]_[中级]_[多态下引用和指针有什么区别]

2020-05-10 04:09 2641 查看

场景

  1. 在开发

    C++
    程序, 经常会用到多态引用和指针. 它们有什么区别吗?

  2. 多态的引用和指针什么时候会调用虚函数表, 什么时候不会?

  3. 多态的引用和指针它们是调用的子类的方法还是父类的方法?

说明

引用变量

  1. 引用变量是

    C++
    相对
    C
    增加的复合类型, 它是
    已定义变量
    的别名,注意是已定义的变量, 必须是指向一个已定义的变量, 因此引用变量在声明时必须初始化。

  2. 引用类型使用

    &
    来声明引用, 类似于指针的
    *
    声明为指针.比如以下的
    ii
    就是一个引用变量.
    int i; int& ii = i;

  3. 引用变量作为

    已定义变量
    的别名,对
    已定义变量
    的任何操作都可以在引用变量里使用,比如取地址, 赋值等; 修改了引用变量等值也就是修改了
    已定义变量
    的值。通常引用变量作为函数参数时,修改函数参数的值即修改了已定义变量的值。比如
    int& ii
    .

多态

  1. 多态, 从字面上可以说明时多种行为形态。一般是指同一个方法,在子类和基类的的行为是不同的,即有继承。

  2. 引用或指针都能利用到多态的特性, 它们调用方法的行为取决于调用该方法的变量是什么类型或者实际对象是什么类型。

  3. 只有方法声明为虚函数时,引用和指针才会调用虚函数表.

  4. 当方法声明为虚函数时,引用和指针会调用实际对象的方法, 比如实际对象是子类, 调用引用变量是基类,那么会调用子类的虚函数。

  5. 而非虚函数的方法,根据调用的方法就是调用变量类型的方法,即使子类重载了基类的方法。比如调用变量是基类指针,指向的对象是子类对象,那么仍然调用的是基类的方法。

  6. 当我们写一个类的时候, 往往需要声明析构函数为虚方法, 这样做的目的是当子类析构时,会自动调用父类的析构函数。有些编译器会自动把每个类的析构函数加上

    virtual
    ,析构时会自动调用父类的析构函数. 比如我这里的
    LLVM 5.1
    .

引用和指针的区别

  1. 引用并不是一个地址, 而指针往往代表一个地址.

  2. 引用必须是一个已定义对象的别名,不能为

    NULL
    .

  3. 类引用一般使用

    .
    作为调用属性方法的符号, 而指针需要使用
    ->
    .

  4. 引用类型和指针类型可以使用

    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 >> 第五版

1.3 对象的差异 << 深度探索C++对象模型 >>

13.4 多态公有继承. << C++ Primer Plus >> 第五版

infoworld 博客专家 原创文章 347获赞 126访问量 120万+ 关注 他的留言板
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: