- 多態
有種說法,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的析構函數。