由C++的多态想到 C++与Golang的一点区别

本文探讨了Golang和C++在多态、继承、组合及map数据结构上的区别。Golang通过组合实现了类似多态的行为,而C++则依赖于虚拟函数。此外,还分析了Golang map与C++ map在实现机制和遍历行为上的不同。
摘要由CSDN通过智能技术生成
  1. 多态

有种说法,Golang没有多态/继承,它叫组合,比如:

type Base struct {
}

func (b *Base) sayHello() {
	b.Hello()
}

func (b *Base) Hello() {
	fmt.Println("Base Hello")
}

type Deverived struct {
	Base
}

func (d *Deverived) Hello() {
	fmt.Println("Deverived Hello")
}

func testDeverived() {
	d := new(Deverived)
	d.sayHello()
}

func main() {
	testDeverived()
}

将输出,

Base Hello

为什么呢?因为在testDeverived函数中,d的类型虽然是Deverived,但Deverived没有实现sayHello方法,而在基类Base的sayHello方法中,此时类型是*Base,因此调用的会是基类的Hello方法。

  1. C++与Golang的map的区别

golang map是hash map, 而C++ map是red-black-tree map;
golang map是无序的, 且每次遍历不稳定一致, 而C++ map是有序的, 且每次遍历稳定一致,这个可以从代码运行情况看出:

func testMap() {
	m := map[string]int{"hello": 1, "world": 2}
	for key, _ := range m {
		fmt.Println(key, m[key])
	}
}

以上代码多次运行的结果有可能不一样。

C++中的多态需要几个条件:

  1. 基类函数前加上virtual;
  2. 子类实现同名函数,包括函数返回值,函数参数类型和参数个数都要一致;

虚函数是动态多态(好像有相对的静态多态),是在运行时才确定的行为。

// base.h
#ifndef BASE_H
#define BASE_H

class Base
{
public:
    Base();

    virtual void sayHi();
    void func();
};

#endif // BASE_H

// base.cpp
#include "base.h"
#include <iostream>
using namespace std;

Base::Base()
{

}

void Base::sayHi()
{
    cout<<"I'm Base"<<endl;
}

void Base::func()
{
    cout<<"Base::func()"<<endl;
}

// deverived.h
#ifndef DEVERIVED_H
#define DEVERIVED_H

#include "base.h"

class Deverived : public Base
{
public:
    Deverived();
    ~Deverived();

    void sayHi();
    void func();
};
#endif // DEVERIVED_H

// deverived.cpp
#include "deverived.h"
#include <iostream>
using namespace std;

Deverived::Deverived()
{

}

Deverived::~Deverived()
{
    cout<<__func__<<endl;
}

void Deverived::sayHi()
{
    cout<<"I'm Deverived"<<endl;
}

void Deverived::func()
{
    cout<<"Deverived::func()"<<endl;
}

以上代码中,sayHi有用virtual修饰,是虚函数,而func没有用virtual修饰,不是虚函数,func的输出取决于调用对象的类型,观察如下代码输出:

Base *b = new Deverived();
b->sayHi();
b->func();

Deverived *d = new Deverived();
d->sayHi();
d->func();
I'm Deverived
Base::func()
I'm Deverived
Deverived::func()

参考

Q:类构造函数可以是虚函数吗?
A:不可以,虚函数的调用需要虚函数表指针,而该指针存放在对象的内容空间中;若构造函数声明为虚函数,那么由于对象还未创建,还没有内存空间,更没有虚函数表地址用来调用虚函数——构造函数了。

Q:为什么析构函数可以为虚函数,如果不设为虚函数可能会存在什么问题?
A:首先析构函数可以为虚函数,而且当要使用基类指针或引用调用子类时,最好将基类的析构函数声明为虚函数,否则可以存在内存泄露的问题。
举例说明:
子类B继承自基类A;A *p = new B; delete p;
1) 此时,如果类A的析构函数不是虚函数,那么delete p;将会仅仅调用A的析构函数,只释放了B对象中的A部分,而派生出的新的部分未释放掉。
2) 如果类A的析构函数是虚函数,delete p; 将会先调用B的析构函数,再调用A的析构函数,释放B对象的所有空间。
补充: B *p = new B; delete p;时也是先调用B的析构函数,再调用A的析构函数。

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值