STL Container 的 destroy

因為工作的緣故,想要了解一下,如果我把物件丟到 STL Container 裡面的時候,C++會怎麼幫我 destroy, 所以寫了以下這支小程式來驗證一下:

#include <iostream>
#include <string>
#include <vector>
class node {
public:
node();
node( string s );
node( char* s );
~node();
private:
string m_string;
};
node::node() {
m_string="";
cout << "initialize " << m_string << endl;
}
node::node(string s):m_string(s) {
cout << "initialize " << m_string << endl;
}
node::node(char* s):m_string(s) {
cout << "initialize " << m_string << endl;
}
node::~node() {
cout << "destroy " << m_string << endl;
}
int main(void) {
vector<node> vector_test1;
vector<node*> vector_test2;
cout << "Non-Pointer Version" << endl;
vector_test1.push_back( node("test1") );
vector_test1.push_back( node("test2") );
vector_test1.push_back( node("test3") );
vector_test1.push_back( node("test4") );
vector_test1.push_back( node("test5") );
vector_test1.push_back( node("test6") );
vector_test1.push_back( node("test7") );
vector_test1.push_back( node("test8") );
vector_test1.push_back( node("test9") );
vector_test1.push_back( node("test10") );
cout << endl << "Pointer Version" << endl;
vector_test2.push_back( new node("test1") );
vector_test2.push_back( new node("test2") );
vector_test2.push_back( new node("test3") );
vector_test2.push_back( new node("test4") );
vector_test2.push_back( new node("test5") );
vector_test2.push_back( new node("test6") );
vector_test2.push_back( new node("test7") );
vector_test2.push_back( new node("test8") );
vector_test2.push_back( new node("test9") );
vector_test2.push_back( new node("test10") );
}

執行以後的結果很有趣.
在我的機器上是這樣子的:

Non-Pointer Version
initialize test1
destroy test1
initialize test2
destroy test1
destroy test2
initialize test3
destroy test1
destroy test2
destroy test3
initialize test4
destroy test4
initialize test5
destroy test1
destroy test2
destroy test3
destroy test4
destroy test5
initialize test6
destroy test6
initialize test7
destroy test7
initialize test8
destroy test8
initialize test9
destroy test1
destroy test2
destroy test3
destroy test4
destroy test5
destroy test6
destroy test7
destroy test8
destroy test9
initialize test10
destroy test10
Pointer Version
initialize test1
initialize test2
initialize test3
initialize test4
initialize test5
initialize test6
initialize test7
initialize test8
initialize test9
initialize test10
destroy test1
destroy test2
destroy test3
destroy test4
destroy test5
destroy test6
destroy test7
destroy test8
destroy test9
destroy test10

這說明了幾件事情:
1.如果我不是利用 new 去建立物件,而是利用宣告建立物件的話
c++只是把建立好的物件複製一份到 container 中,接著就釋放他了
如果是利用 new 去建立物件,那麼在程式的最後,container 會自動呼叫每個元素的 解構子 destroyer 去解構,而無須我們再手動去解構他們.
2.vector 會自我成長,但是在成長的時候,會同樣利用複製的方式,把原本在 container 的資料複製到新的 container 裡面去,但這樣子不是很有效率

RPM製作簡單說明

在此直接以我在公司開發的 lcd daemon 來作為實例
首先把我的 lcd daemon 編譯過,得到 lcdctld, 假設為 1.0.0 版,release 第一次
接著,隨便在某個地方建立目錄,命名為 lcdctld-1.0.0
在裡面建立 /usr/sbin
然後把 lcdctld 放到 lcdctld-1.0.0/usr/sbin 裡面去
利用
tar cvzf lcdctld-1.0.0.tar.gz lcdctld-1.0.0
這個指令得到 tar.gz 檔案
把這個檔案丟到 /usr/src/redhat/SOURCES 裡面去
接著就是最重要的地方了
要編輯 lcdctld.spec 檔案
內容如下:

%define name lcdctld
%define ver 1.0.0
Summary: MS-9507 LCD Daemon
Name: %{name}
Version: %{ver}
Release: 1
License: Micro Star Inc.
Group: System Environment/Daemons
BuildRoot: %{_tmppath}/%{name}-%{ver}-root%
description
lcdctld is a daemon that listen MS-9507 device.
%prep
%setup -q
%build
%install
rm -rf $RPM_BUILD_ROOT
%{__mkdir} $RPM_BUILD_ROOT/
%{__mkdir} $RPM_BUILD_ROOT/usr
%{__mkdir} $RPM_BUILD_ROOT/usr/sbin
install -m555 usr/sbin/lcdctld $RPM_BUILD_ROOT/usr/sbin
rm -f tmp
%cleanrm -rf $RPM_BUILD_ROOT
%files/usr/sbin/lcdctld

這裡面有很多東西,建議直接找個範本來修改比較好,我是直接拿 thttpd 的來作修改.
主要要修改的有上面的欄位:Summary, License, Name, Version, Group, …等等的
還有 %install 開始的地方,這裡主要就是寫複製檔案的 script.事實上, RPM 會自動把你 SOURCES 下的 tar.gz 檔解開,所以你只要把對應的檔案複製到 RPM_BUILD_ROOT 對應的目錄去就好了.
最後還有 %files ,如果這裡沒有東西,rpm 會發生錯誤.這裡主要是放檔案的列表,讓 rpm 知道這個 package 裡面有什麼檔案.
編輯好 lcdctld.spec 之後,輸入
rpm -bb lcdctld.spec
順利的話,你應該會在 /usr/src/redhat/RPMS/i386 目錄下看到 lcdctld-1.0.0-1.rpm 才對~~
你可以使用
rpm -Uvh lcdctld-1.0.0-1.rpm
來安裝
然後使用
rpm -e lcdctld-1.0.0-1
來移除
.spec 裡面的一些用法
%define -> 定義一些變數
%{xxx} -> 引用變數
%{_tmppath} -> 會指到 /var/tmp 下面
%{__command}-> shell的指令幾乎都可以引用,只是前面要加上兩個底線,例如 mkdir 就是 %{__mkdir}.
%prep
%setup
%build
%install
%clean
%files -> 這些都是表示特定的步驟,雜誌上只說在 %install, %clean, %files 要加東西而已,其他不清楚
我想這樣子在目前的使用上,應該就足夠了吧~
將來如果有新的發現,會在這個主題上繼續累加~
資料參考來源: Linuxer 雜誌第 12 期以及 Linux in a NutShell
Linux in a NutShell 只有說明 rpm 指令有哪些參數而已,完全沒有說明 .spec 檔要如何編輯