談談c++的初始化工作(2) (轉)
我們首先來看上次遺留的問題。
把(1)中的程式碼換為註釋部分,或許您一時還認識不到會有什麼發生,但最終是通不過的,丟擲異常,資訊如下:
未處理的“System.Runtime.InteropServices.SEHException”型別的異常出現在 TestInit.exe 中
其他資訊:外部發生異常。
This application has requested the Runtime to tenate it in an unusual way.
Please contact the application's support team for more information.
Press any key to continue
我想,您回頭再細看的話,就會明白為什麼如此了(我們寫一定要追問到底:)。
我們今天要談的是,一些變數只有唯一的初始化形式,透過例子,告訴您要特別注意。然後,我們就一步一步,來看資源淺複製的問題。我相信初學c++的同學,會對“複製”有些疑問,它就是為了解決上述問題的;但事實上,還有一個隱藏的地方,今天我也想給您指出。
這些程式,可是我特意設計的哦。希望可以很方便的認識問題所在,與解決之道。
首先,看第一個例子。在類中,這兩類變數:
e.g.
Name &name; //引用
const int ID; //常量
它們的初始化形式是唯一的。而且必須由您來初始化。
看下面的程式:
//human.h
#pragma once
class Name
{
char *name;
public:
//...
};
class Human
{
Name &name;
const int ID;//每個人都唯一的標誌號
//...
public:
Human(void);
~Human(void);
//...
};
//human.cpp
#include "human.h"
#using
//預設的建構函式
Human::Human(void)
{
}
Human::~Human(void)
{
}
寫一個主測試。
但除錯出錯,錯誤資訊檔案為:
/*----------------------------------------------------------------------------
//Human:error file
------ 已啟動生成:專案:TestInit, :De ------
正在編譯...
Human.cpp
Human.cpp(5) : error C2758: “Human::name” : 必須在建構函式基/成員初始值設定項列表中初始化
e:Small_codeTestInitHuman.h(13) : 參見“Human::name”的宣告
Human.cpp(5) : error C2758: “Human::ID” : 必須在建構函式基/成員初始值設定項列表中初始化
e:NETSmall_codeTestInitHuman.h(14) : 參見“Human::ID”的宣告
fmain.cpp
Date.cpp
正在生成程式碼...
生成日誌儲存在“file://e:NETSmall_codeTestInitDebugBuildLog.htm”中
TestInit - 2 錯誤,0 警告
---------------------- 完成 ---------------------
生成:0 已成功, 1 已失敗, 0 已跳過
--------------------------------------------------------------------------------
*/
因為這裡涉及的是僅僅的c++語法,我就不多費口舌了,如何改正,希望您能動手試試,一定要動手,不要想當然哦~~~
當然,如果您是愛問題的人,我想您可以這樣深究一下:設計c++語言時,為什麼諸如int型別成員變數能提供預設初始化,而它們卻不能;從編譯角度,刻意給它們提供如int型別般的初始化會有什麼困難和問題?
下面詳細談什麼是資源淺複製問題。沿襲c的習慣,c++對自分配的資源進行統一管理,但是,申請的資源,則有使用者來釋放。
比如:
userType *p=new userType(/*---*/);
//...
delete p;
//delete釋放一般是不可忘的
單獨的變數或許對您來說是不成問題的。但在類中,這些情況就變的相當複雜。處理不好,您的系統要麼就是因為洩露而執行不下去,而要麼就是異常頻頻發生。
我們先來看一些c++的預設操作:
//...
class OneClass {
int _value;
public:
OneClass(int _val=0):_value(_val) {}
~OneClass() {}
//...
};
//you may use in this way:
OneClass oneObj(7);
OneClass anotherObj;
anotherObj=oneObj;//(1)
//...
//int Compare(OneClass one,OneClass two);
int k=Compare(oneObj,anotherObj);//(2)
//...
在本程式的場景下,上面的程式碼是可以完好工作的,但您清楚(1)與(2)系統都作了什麼了嗎?您是否知道,如果您的初始化工作做的不好的話,即使,就沿用上面的初始化習慣,您的程式很容易就崩潰了呢。
答案是,(1)語句時,預設的,系統試圖把oneObj的資源全部copy給anotherObj,但使用者申請的資源(new來的:),卻傳入的是地址;(2)語句的預設形參傳遞遵循同樣的規則。
當然這與與是不同的,因為java與c#的是引用型別。而c++,除非您強行定義為引用型別的,它就不是。
我們來看下面的例子,第一遍我建議您只看程式,不要往下看,看您能否發現什麼問題。
//human.h
#pragma once
#define NULL 0
class Name
{
char *name;
public:
Name(char *_name=NULL):name(_name) {}
~Name() { }
char *getName(){ return name;}
};
class Human
{
Name *name;//
int ID; //唯一化標誌
public:
Human(int id=0,char *_name=NULL);
~Human(void);
int getID()const { return ID;}
Name *getName() { return name;}
};
//human.cpp
#include "human.h"
#using
Human::Human(int id,char *_name):ID(id)
{
name=new Name(_name);//初始化:指標
}
Human::~Human(void)
{
delete name;//析構時釋放資源
}
//fmain.cpp
#include
#include "human.h"
void main()
{
//測試程式
try{
Human lily(11100120,"lily");
Human lucy=lily;
}
catch(...)
{//如果有any異常
std::cout< }
}
//請回頭看程式,您覺得一切都好嗎?
事實上,除錯過程中,等三個異常忽略後,就會得到下面的結果:
/*
After three exceptions occured you get :
Unknown Exception...
Press any key to continue
*/
為什麼?
看這幾行程式碼:
Human lily(11100120,"lily");
Human lucy=lily;
雖然一開始,我就給了小例子,形式一樣,那個沒問題。何以這個就不行了呢?因為類的定義不同。
由c++工作的機理,這行
Human lucy=lily;
是把lily的資源複製給lucy(lucy是不建構函式的),可是,因為其中的name是使用者申請的資源,並不能把它也複製過去,而是直接傳了地址。這樣,您知道嗎,lucy.name和lily.name的地址是一樣的。
於是,當一個的解構函式呼叫後,name所指向的資源已被釋放掉了的。而另外一個類的解構函式又去釋放,問題來了---程式崩潰了!
這就是淺複製問題---“淺”的不完全的複製:)。
找到原因,我們就辦法。解決的辦法是,這份工作自己來做!
寫一個複製賦值操作(public):
形式為: className &operator=(className &obj){ /*...*/}
您看下面的解決辦法和結果:
/*
If you add ...in class Human(public):
Human &operator=(Human &human){
if(this!=&human){
ID=human.getID();
name=new Name(human.getName()->getName());
return *this;
}
}
OK,and you get:
Press any key to continue
That is what we want!
*/
下面的例子,是複製函式的問題,在上面的基礎上,我改動了一下程式的結構,
定義了一個名空間。
具體的問題分析我留給下次,您就有機會細細的看了。您是否一切都清楚了?
您可以解決這個問題嗎?
//human.h
#pragma once
#using
namespace Humanbeing
{
#define NULL 0
class Name
{
char *name;
public:
Name(char *_name=NULL):name(_name) {}
~Name() { }
char *getName(){ return name;}
};
class Human
{
Name *name; //
int ID; //唯一的標誌
public:
Human(int id=0,char *_name=NULL):ID(id)
{
name=new Name(_name);//申請資源
}
~Human(void)
{
delete name;//釋放資源
}
//複製賦值操作
Human &operator=(Human &human){
if(this!=&human){
ID=human.getID();
name=new Name(human.getName()->getName());
return *this;
}
}
int getID()const { return ID;}
Name *getName() { return name;}
};
//名空間裡的函式
bool IsSameMan(Human one,Human another)
{
if(one.getID()==another.getID())
return true;
else return false;
}
}
//測試檔案
#include
#include "human.h"
void main()
{
using namespace Humanbeing;
try{
Human lily(11100120,"lily");
Human lucy=lily;
if(IsSameMan(lucy,lily))
{
std::cout< }else
std::cout<
}
catch(...)
{
std::cout< }
}
除錯結果呢,是連續六個異常後,出現:
After six exceptions occured you get :
They are the same one.
Unknown Exception...
Press any key to continue
為什麼(上次異常是三個,這次是六個,可以解釋嗎)?怎麼辦?
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-957963/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 談談c++的初始化工作(1) (轉)C++
- 談談c++的初始化工作(3) (轉)C++
- 談談c++的初始化工作(4) (轉)C++
- 談談專案的成本管理2 (轉)
- 談談 C++ STL 中的迭代器C++
- 談談C++中的swap函式C++函式
- 談談工作中的設計模式設計模式
- 談談軟體專案管理的重要性2(轉)專案管理
- 再談c++型別轉換C++型別
- 談談創業公司技術的工作模式創業模式
- 談談對BPM的理解(轉)
- Matt Pietrek訪談,談他對.NET的看法及Numega工作情況(英文) (轉)
- 談談字串翻轉字串
- 談談專案的成本管理 (轉)
- 談談專案的成本管理(轉)
- 談談谷歌word2vec的原理谷歌
- 談談 ajax 工作過程那點事
- C/C++的思索 C++之父訪談錄 [上帝的玩笑嗎?] (轉)C++
- C++之父Bjarne談C++在2005年(轉)C++JAR
- 談談我工作中的23個設計模式設計模式
- 談談BPM、工作流引擎與OA的關係
- 談談專案的成本管理3(轉)
- 談談redhat9 KDE的漢化(轉)Redhat
- 談談專案群組的管理 (轉)
- 談談Spring中的BeanPostProcessor介面(轉)SpringBean
- C++中的enum淺談C++
- 淺談國外工程專案的投標工作(轉)
- 談一談安全運營工作是什麼
- 近期的爬蟲工作雜談爬蟲
- 工作隨筆雜談
- C++程式設計雜談:物件導向 (轉)C++程式設計物件
- 談談IT專案中的溝通管理(轉)
- 談談 HTTP/2 的協議協商機制HTTP協議
- 談談SQL 語句的優化技術 (2)SQL優化
- 程式設計師的經驗之談-生活與工作(轉)程式設計師
- 從移交驗收的角度談工程專案收尾工作(轉)
- C++ string (淺談)C++
- 談談App的統一跳轉和ARouterAPP