- 多态
有种说法,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方法。
- 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++中的多态需要几个条件:
- 基类函数前加上virtual;
- 子类实现同名函数,包括函数返回值,函数参数类型和参数个数都要一致;
虚函数是动态多态(好像有相对的静态多态),是在运行时才确定的行为。
// 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的析构函数。