發現程序題也挺有價值的。ios
順便記錄下來幾道。函數
#include <iostream> #include <cstring> using namespace ① std ; void Swap(char * const str1, char * const str2) { // 形參爲鎖定目標的指針(即指針常量,亦即指針的指向不能改變,內容可更改) int n = strlen(② str1 ) + 1; char *temp = new char[③ n ]; ④ strcpy(temp, str1) ; ⑤ strcpy(str1, str2) ; ⑥ strcpy(str2, temp) ; delete [] temp; } void Exchange(const char *&str1, const char *&str2) { // 形參不使用二級指針,而使用指針的常引用(常量指針的別名) ⑦ const char *temp; ⑧ temp = str1 ; ⑨ str1 = str2 ; ⑩ str2 = temp ; } int main() { char str1[80] = "Tom", str2[80] = "Jerry"; const char *p1 = "Tom", *p2 = "Jerry"; cout << str1 << '\t' << str2 << endl; cout << p1 << '\t' << p2 << endl; Swap(str1, str2); Exchange(p1, p2); cout << str1 << '\t' << str2 << endl; cout << p1 << '\t' << p2 << endl; return 0; }
這題應該是要搞清楚const char *p; 和 char * const p;this
const char *p能夠這麼理解,const後面是char*,因此char*對應的內容就不能改變;而char* const p的const後面是p,因此p的值不能改變,p的值是一個指針地址。綜上,緊接着const後面的內容就是不可改變的內容。這下應該能夠作題了。spa
swap函數的const後跟的是指針,因此指針地址不能改變,而其指向地址對應的值是能夠改變的,因此,咱們只能想辦法改變str1和str2指向的值,因爲能夠調用strcpy進行賦值,中間變量天然就是對應的char * const temp,這裏圖中沒用const也能夠,但我感受要對應,仍是加上const 最好。固然,我已經改過以後跑過了。個人理解是對的。操作系統
exchange函數的const後跟的是char*,這就意味着字符串的值是不能改變的,那麼咱們就只能交換兩個別名對應的地址了,注意不要搞混,別名的綁定是不能改變的,咱們只能改變別名對應的地址。臨時變量也應該對應 const char* temp;這樣就對了。指針
#include <iostream> using namespace std; class ctest { public: ctest(int x=0) : a(x) { cout << "構造對象(" << a << ")\n"; } ~ctest(){ cout << "析構對象(" << a << ")\n";} ctest & Add() // 本成員函數爲引用返回 { ++a; return *this; } ctest add() // 本成員函數爲值返回 { ctest temp(*this); ++a; return temp; } friend ostream & operator<<(ostream &out, const ctest &c) { out << c.a; return out; } private: int a; }; int main() { ctest a(100), b(200); cout << a << ", " << b << endl; // 第3行輸出 a.Add().Add(); b.add().add(); // 拷貝構造臨時無名對象時無輸出 cout << a << ", " << b << endl; // 第6行輸出 a.~ctest(); // 主動調用析構函數,並不銷燬對象 b.~ctest(); // 主動調用析構函數,並不銷燬對象 cout << a << ", " << b << endl; // 對象a,b仍可被訪問。第9行輸出 cout << "返回操做系統。" << endl; // 第10行輸出 return 0; } /* 構造對象(100) 構造對象(200) 100, 200 析構對象(200) 析構對象(201) 102, 201 析構對象(102) 析構對象(201) 102, 201 返回操做系統。 析構對象(201) 析構對象(102) */
這個題考輸出,要注意的是無名對象的析構問題,也就是第四和第五行的輸出,咱們來逐行分析代碼,首先建立a和b對象,分別初始化爲100和200,這時候會調用二者的構造函數,所以輸出兩行構造函數該輸出的東西。而後輸出a和b的值,「100,200」,再往下執行,調用a.Add()函數,是引用返回,可是返回是其自己,期間沒有建立中間變量,也就是說a.Add執行完了以後返回的結果仍是a,而後後面就再執行一個a.Add,這時候返回的仍是a,可是a對象的成員值自增了兩次,已經變成102了,期間沒有對象銷燬,所以沒有析構,沒有輸出。再往下執行,調用b.add函數,根據函數定義咱們知道,函數執行過程當中先拷貝構造一個temp對象,可是沒有寫拷貝構造函數,所以不輸出任何內容,且默認是淺拷貝,而後b的成員值自增變爲201,但這時候temp的成員值仍是200,由於拷貝過去以後會從新分配空間,因此temp的成員值和b的成員值不是一個地址,而後返回temp,且是值返回,值返回以後其實temp尚未析構,而是做爲一個無名變量繼續使用,而後這個無名變量再調用add函數,致使的是temp對象的成員值+1=201,而這時候就已經和對象b無關了。可是要注意的是,因爲b調用的add是值返回,b.add().add()結束以後呢,兩個無名變量就沒用了,就會被銷燬(以前講過,無名變量在語句結束以後被銷燬,是整條語句),因此析構兩個無名對象,無名對象的析構順序又和正常對象的析構順序恰好相反(這個我專門查了資料),因此是依次析構,所以輸出的是200和201,這裏惟一要理解的是,值返回的對象的析構時間並非出了函數體,而是執行完調用這個函數的語句,才被析構。再往下走,輸出a和b的成員值,a是102,b是201,而後a主動析構,輸出「析構對象102」,而後b主動析構,輸出「析構對象201」,注意,這裏是主動析構,析構函數裏面除了輸出什麼也沒有,只是至關於調用了一個輸出函數。下一步輸出a、b的成員值,仍是102和201,而後輸出返回操做系統。下一步結束main函數,這時候纔是a、b真正的析構時間,b先析構,而後是a。就很簡單了。code
#include <iostream> using namespace std; class Base // 基類 { public: Base(int x=0) : a(x) { cout << "構造基類對象(" << a << ")\n"; } Base(const Base &b) : a(b.a) { cout << "拷貝構造基類的對象(" << a << ")\n"; } virtual ~Base() { cout << "析構基類對象(" << a << ")\n"; } protected: int a; static int num; // 靜態數據成員 }; int Base::num = 0; // 靜態數據成員定義及初始化 class Derived : public Base // 派生類 { public: Derived(int x=0, int y=0) : Base(x), b(y) { // 構造函數中輸出了對象個數[num],在園括號以前 cout << "構造派生類的對象[" << ++num << "](" << a << ", " << b << ")\n"; } Derived(const Derived &d) : Base(d), b(d.b) { // 拷貝構造函數中輸出了對象個數[num],在園括號以前 cout << "拷貝構造派生類的對象[" << ++num << "](" << a << ", " << b << ")\n"; } ~Derived() // 析構函數中輸出了對象個數[num],在園括號以後 { cout << "析構派生類的對象(" << a << ", " << b << ")[" << --num << "]\n"; } void Set(int x, int y) { a = x; b = y; } friend ostream &operator<<(ostream &out, const Derived &d) { out << '(' << d.a << ", " << d.b << ')'; return out; } private: int b; }; void f(Derived &r, const Derived *p, Derived x) { // 形參分別爲引用傳遞、指針傳遞、值傳遞 cout << "in f function..." << endl; cout << r << endl; r.Set(50, 80); // 引用傳遞的對象,值被重置 cout << *p << endl; // 雖然從指針p看其目標爲常對象,可是…… cout << x << endl; x.Set(0, 0); cout << x << endl; } int main() { Derived d(100, 200); cout << "Calling f function..." << endl; f(d, &d, d); // 請注意:用同一個對象或對象的地址值作實參 cout << "return to Operating System." << endl; return 0; } /* 構造基類對象(100) 構造派生類的對象[1](100, 200) Calling f function... 拷貝構造基類的對象(100) 拷貝構造派生類的對象[2](100, 200) in f function... (100, 200) (50, 80) (100, 200) (0, 0) 析構派生類的對象(0, 0)[1] 析構基類對象(0) return to Operating System. 析構派生類的對象(50, 80)[0] 析構基類對象(50) */
仍是至上而下分析,先初始化一個派生類對象d,賦值100,200,這時候先執行基類的構造函數,輸出「構造基類對象100」,而後執行派生類構造函數,輸出「構造派生類對象[1](100,200)」,而後進入函數,輸出第三行,進入函數的過程當中,第一個參數是引用傳遞,不構建對象,第二個參數是值傳遞的地址,也沒有構造對象,所以這二者都不會調用構造函數,且二者指向的是同一對象;而第三個參數是值傳遞,傳遞的是對象,所以會產生實參是臨時對象,會爲實參構建對象,實參調用構造函數,分別輸出第四行和第五行。接下來輸出第六行,而後輸出r的值,(100,200),而後r調用set函數從新賦值,輸出p成員的值,剛纔說過,r和p指向同一對象,雖然經過p不能改變其成員的值,可是經過r能夠改,所以輸出set以後的值(50,80)。接下來輸出x,x是個臨時對象,其值是構造時候的(100,200),輸出。而後x調用其set函數,將臨時對象的成員值都設爲 了0。輸出x,固然是兩個0了。出函數,臨時變量該析構了。按照析構的順序先析構派生類,再析構基類。輸出兩行析構。接着往下走,cout一行英文,出main函數,該析構對象d了,輸出兩行析構,結束。對象
就三道啊,寫不動了,讓我休息會。blog