class base
int get_v_b() const
private:
int _v_b;
};class derived :
public base
int get_v_d() const
private:
int _v_d;
};
base是derived的基類,前者擁有成員變數_v_b,後者擁有前者的_v_b和自己定義的_v_d。
我們分別構建乙個base和derived物件陣列
base * build_base_list(size_t count)
for (size_t i = 0; i < count; i++)
return b_list;
}derived * build_derived_list(size_t count)
for (size_t i = 0; i < count; i++)
return d_list;
}
我們再提供乙個方法,用於遍歷陣列中物件的_v_b(base基類定義的成員變數)。
void print_v_b(base *b_list, size_t b_list_count)
for (size_t i = 0; i < b_list_count; i++)
}
然後我們對構建的兩個陣列分別呼叫print_v_b,以期望列印出各自的v_b。
const size_t count = 8;
std::unique_ptr > base_list(
build_base_list(count),
(base* p)
);std::cout << "base_list:" << std::endl;
print_v_b(base_list.get(), count);
std::unique_ptr > derived_list(
build_derived_list(count),
(derived* p)
);std::cout << "derived_list:" << std::endl;
print_v_b(derived_list.get(), count);
理論上,我們將看到兩組相同的結果。因為base_list和derived_list中每個元素的_v_b是其在陣列中的下標。然而結果是
base_list:
_v_b(0):0
_v_b(1):1
_v_b(2):2
_v_b(3):3
_v_b(4):4
_v_b(5):5
_v_b(6):6
_v_b(7):7
derived_list:
_v_b(0):0
_v_b(1):0
_v_b(2):1
_v_b(3):1
_v_b(4):2
_v_b(5):2
_v_b(6):3
_v_b(7):3
很明顯,derived_list陣列輸出的元素資訊不正確。
derived_list陣列中的每個元素都是base子類derived的物件。理論上,對derived物件,通過基類base的方法訪問,是可以獲得正確資料的。那問題出在**?我們還要回到print_v_b方法中
void print_v_b(base *b_list, size_t b_list_count)
for (size_t i = 0; i < b_list_count; i++)
}
我們看到第7行是通過陣列下標的形式獲取每個元素的。在c語言中,如果乙個陣列通過下標訪問元素,其獲取的元素實際位址是head+index*sizeof(struct)。
我們分別看乙個int型和long long型陣列通過下標獲取元素的取址值
const size_t count = 8;
int integer_list[count];
std::cout << "head:" << integer_list << " sizeof(int):" << sizeof(int) << std::endl;
for (size_t i = 0; i < count; i++)
long long longlong_list[count];
std::cout << "head:" << integer_list << " sizeof(int):" << sizeof(long long) << std::endl;
for (size_t i = 0; i < count; i++)
可以看到,雖然每次下標只是自增1,但是位址實際增加了每個元素的大小。
head:0x7fffffffe900 sizeof(int):4
integer_list[0] address:0x7fffffffe900
integer_list[1] address:0x7fffffffe904
integer_list[2] address:0x7fffffffe908
integer_list[3] address:0x7fffffffe90c
integer_list[4] address:0x7fffffffe910
integer_list[5] address:0x7fffffffe914
integer_list[6] address:0x7fffffffe918
integer_list[7] address:0x7fffffffe91c
head:0x7fffffffe900 sizeof(int):8
longlong_list[0] address:0x7fffffffe9a0
longlong_list[1] address:0x7fffffffe9a8
longlong_list[2] address:0x7fffffffe9b0
longlong_list[3] address:0x7fffffffe9b8
longlong_list[4] address:0x7fffffffe9c0
longlong_list[5] address:0x7fffffffe9c8
longlong_list[6] address:0x7fffffffe9d0
longlong_list[7] address:0x7fffffffe9d8
在print_v_b陣列中,它預設認為陣列中每個元素大小是base物件的大小。然而derived_list陣列中每個元素的是derived物件大小。derived模擬base類多乙個元素_v_d,從而大小從base物件的4位元組變成了8位元組。這樣第7行中,每次下標移動實際只是移動了4位元組,於是每個奇數次移動均移動到derived物件的_v_d前,每個偶數次移動均移動到derived物件的_v_b前。這就出現了上面的資料錯亂的問題。
陣列是c的遺產。為了相容c,c++保留了很多c語言的印記,於是導致自身呈現出一些不清晰的表達。比如下面如下三種寫法
void print_t(t *t)
void print_t(t t)
void print_t(t & t)
第3種寫法,我們可以知道t是個物件。
第2種寫法,我們可以知道t表達了乙個陣列。
第1中寫法,則可以表達出t可以是乙個陣列,可以是乙個物件。那麼到底它是個組數還是物件?我們沒法從語法上得知。
像本例中,使用者很有可能會把print_v_b的第一元素當成乙個物件指標(當然第二個引數透露出其應該是乙個陣列,但是假如沒有第二個引數呢?),那麼他怎麼也不會想到,對derived_list呼叫print_v_b會出錯。
這從乙個側面可以說明,對於可以靈活表達的c++語言,我們需要採用一些易於理解的方式去設計api。
寫給那個茶水妹的《乘風破浪》誕生記
去年秋冬,one實驗室作者生活在 乘風破浪 劇組中間,見證了拍攝過程,本文講述的就是電影誕生的故事。這也是茶水妹和她的夥伴們,包括導演和主演們的故事。正是三百多個年輕人像候鳥一樣來到亭林鎮附近,帶著他們年輕的人生,發生了一場年輕的化學反應,才造就了 乘風破浪 2016年,上海,初秋,穿過城市的風帶著...
菜鳥類庫誕生記一 值型別的擴充套件
自從上次一篇部落格已經有兩個月了,因為一些事耽擱了 出發前說說我的個人感想 到上海之後參加了很多面試,不管從技術上還是專案經驗都遭遇到了一些打擊,所以決心打造屬於自己的框架作品。雖然我還是乙個菜鳥,不過我相信只要堅持就會實現我的目標。今天的內容會很簡單,只是想在部落格上做乙個簡單的記錄。此擴充套件方...
菜鳥類庫誕生記一 值型別的擴充套件
自從上次一篇部落格已經有兩個月了,因為一些事耽擱了 出發前說說我的個人感想 到上海之後參加了很多面試,不管從技術上還是專案經驗都遭遇到了一些打擊,所以決心打造屬於自己的框架作品。雖然我還是乙個菜鳥,不過我相信只要堅持就會實現我的目標。今天的內容會很簡單,只是想在部落格上做乙個簡單的記錄。此擴充套件方...