C++學習筆記8-操作符&指標,學習筆記8-操作符
1. 重載操作符
賦值操作符的傳回型別應該與內建類型賦值運算返回的類型相同。內建類型的賦值運算返回對右運算元的引用,因此,賦值操作符也返回對同一類類型的引用。例如,Sales_item的賦值操作符可以聲明為:
class Sales_item { public: // other members asbefore // equivalent to thesynthesized assignment operator Sales_item&operator=(const Sales_item &); };
2. 合成賦值操作符
合成賦值操作符與合成複製建構函式的操作類似。它會執行逐個成員賦值:右運算元對象的每個成員賦值給左運算元對象的對應成員。
除數組之外,每個成員用所屬類型的常規方式進行賦值。對於數組,給每個數組元素賦值。
3. 何時調用解構函式
動態分配的對象只有在指向該對象的指標被刪除時才撤銷。如果沒有刪除指向動態對象的指標,則不會運行該對象的解構函式,對象就一直存在,從而導致記憶體流失,而且,對象內部使用的任何資源也不會釋放。 當對象的引用或指標超出範圍時,不會運行解構函式。只有刪除指向動態指派至的指標或實際對象(而不是對象的引用)超出範圍時,才會運行解構函式。 撤銷類對象時會自動調用解構函式:
// p points todefault constructed object Sales_item *p = newSales_item; { // new scope Sales_item item(*p);// copy constructor copies *p into item delete p; //destructor called on object pointed to by p } // exit localscope; destructor called on item //撤銷一個容器(不管是標準庫容器還是內建數組)時,也會運行容器中的類類型元素的解構函式: { Sales_item *p = newSales_item[10]; // dynamically allocated vector<Sales_item> vec(p, p + 10); //local object // ... delete [] p; // arrayis freed; destructor run on each element }
4. 何時編寫顯式解構函式
許多類不需要顯式解構函式,尤其是具有建構函式的類不一定需要定義自己的解構函式。僅在有些工作需要解構函式完成時,才需要解構函式。
解構函式通常用於釋放在建構函式或在對象生命期內擷取的資源。
如果類需要解構函式,則它也需要賦值操作符和複製建構函式,這是一個有用的經驗法則。這個規則常稱為三法則,指的是如果需要解構函式,則需要所有這三個複製控製成員。
5. 如何編寫解構函式
在類名字之前加上一個代字型大小(~),它沒有返回值,沒有形參。解構函式與複製建構函式或賦值操作符之間的一個重要區別是,
即使我們編寫了自己的解構函式,合成解構函式仍然運行。例如,可以為Sales_item: 類編寫如下的空解構函式:
class Sales_item { public: // empty; no work todo other than destroying the members, // which happensautomatically ~Sales_item() { } // other members asbefore };
撤銷Sales_item 類型的對象時,將運行這個什麼也不做的解構函式,它執行完畢後,將運行合成解構函式以撤銷類的成員。
合成解構函式調用string 解構函式來撤銷string 成員,string解構函式釋放了儲存isbn 的記憶體。
units_sold 和 revenue 成員是內建類型,所以合成解構函式撤銷它們不需要做什麼。
6. 管理指標
在類的實現中。包含指標的類需要特別注意複製控制,原因是複製指標時只複製指標中的地址,而不會複製指標指向的對象。
指標可能出錯:
設計具有指標成員的類時,類設計者必須首先需要決定的是該指標應提供什麼行為。將一個指標複製到另一個指標時,兩個指標指向同一對象。當兩個指標指向同一對象時,可能使用任一指標改變基礎對象。類似地,很可能一個指標刪除了一對象時,另一指標的使用者還認為基礎對象仍然存在。
指標成員預設具有與指標對象同樣的行為。然而,通過不同的複製控制策略,可以為指標成員實現不同的行為。大多數C++ 類採用以下三種方法之一管理指標成員:
1. 指標成員採取常規指標型行為。這樣的類具有指標的所有缺陷但無需特殊的複製控制。
2. 類可以實現所謂的“智能指標”行為。指標所指向的對象是共用的,但類能夠防止懸垂指標。
3. 類採取值型行為。指標所指向的對象是唯一的,由每個類對象獨立管理
7. 如何管理指標
A.定義智能指標類
a.引入使用計數
每次建立類的新對象時,初始化指標並將使用計數置為1。當對象作為另一對象的副本而建立時,複製建構函式複製指標並增加與之相應的使用計數的值。對一個對象進行賦值時,賦值操作符減少左運算元所指對象的使用計數的值(如果使用計數減至0,則刪除對象),並增加右運算元所指對象的使用計數的值。最後,調用解構函式時,解構函式減少使用計數的值,如果計數減至0,則刪除基礎對象。
// private class for use by HasPtr only class U_Ptr { friend class HasPtr; int *ip; size_t use; 625 U_Ptr(int *p): ip(p),use(1) { } ~U_Ptr() { delete ip;} }; /* smart pointerclass: takes ownership of the dynamically allocated * object to which it isbound * User code must dynamically allocate an object to initialize a HasPtr * and must not deletethat object; the HasPtr class will delete it */ class HasPtr { public: // HasPtr owns the pointer; p must have been dynamically allocated HasPtr(int *p, inti): ptr(new U_Ptr(p)), val(i) { } // copy members andincrement the use count HasPtr(const HasPtr&orig): ptr(orig.ptr),val(orig.val) { ++ptr->use; } HasPtr&operator=(const HasPtr&); // if use count goesto zero, delete the U_Ptr object ~HasPtr() { if(--ptr->use == 0) delete ptr; } private: U_Ptr *ptr; // pointsto use-counted U_Ptr class int val; };
b. 賦值與使用計數.賦值操作符比複製建構函式複雜一點:
HasPtr&HasPtr::operator=(const HasPtr &rhs) { ++rhs.ptr->use; //increment use count on rhs first if (--ptr->use ==0) delete ptr; // if usecount goes to 0 on this object,delete it ptr = rhs.ptr; //copy the U_Ptr object val = rhs.val; //copy the int member return *this; }
c. 改變其他成員.現在需要改變訪問int* 的其他成員,以便通過U_Ptr 指標間接擷取int:
class HasPtr { public: // copy control andconstructors as before // accessors mustchange to fetch value from U_Ptr object int *get_ptr() const{ return ptr->ip; } int get_int() const {return val; } // change theappropriate data member void set_ptr(int *p){ ptr->ip = p; } void set_int(int i) {val = i; } // return or changethe value pointed to, so ok for const objects // Note: *ptr->ipis equivalent to *(ptr->ip) 628 int get_ptr_val()const { return *ptr->ip; } void set_ptr_val(inti) { *ptr->ip = i; } private: U_Ptr *ptr; // pointsto use-counted U_Ptr class int val; };
B. 定義值型類
處理指標成員的另一個完全不同的方法,是給指標成員提供值語義。具有值語義的類所定義的對象,其行為很像算術類型的對象:複製值型對象時,會得到一個不同的新副本。對副本所做的改變不會反映在原有對象上,反之亦然。string類是值型類的一個例子。
要使指標成員表現得像一個值,複製HasPtr 對象時必須複製指標所指向的對象:
/* * Valuelike behavioreven though HasPtr has a pointer member: * Each time we copy aHasPtr object, we make a new copy of the * underlying intobject to which ptr points. */ class HasPtr { public: // no point topassing a pointer if we're going to copy it anyway // store pointer to acopy of the object we're given 630 HasPtr(const int&p, int i): ptr(new int(p)), val(i) {} // copy members andincrement the use count HasPtr(const HasPtr&orig): ptr(new int(*orig.ptr)), val(orig.val) { } HasPtr&operator=(const HasPtr&); ~HasPtr() { deleteptr; } // accessors mustchange to fetch value from Ptr object int get_ptr_val()const { return *ptr; } int get_int() const {return val; } // change theappropriate data member void set_ptr(int *p){ ptr = p; } void set_int(int i) {val = i; } // return or changethe value pointed to, so ok for const objects int *get_ptr() const{ return ptr; } void set_ptr_val(intp) const { *ptr = p; } private: int *ptr; // pointsto an int int val; };
複製建構函式不再複製指標,它將分配一個新的int 對象,並初始化該對象以儲存與被複製對象相同的值。每個對象都儲存屬於自己的int 值的不同副本。因為每個對象儲存自己的副本,所以解構函式將無條件刪除指標。 賦值操作符不需要分配新對象,它只是必須記得給其指標所指向的對象賦新值,而不是給指標本身賦值:
HasPtr&HasPtr::operator=(const HasPtr &rhs) { // Note: Every HasPtris guaranteed to point at an actual int; // We know that ptrcannot be a zero pointer *ptr = *rhs.ptr; //copy the value pointed to val = rhs.val; //copy the int return *this; }<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>
換句話說,改變的是指標所指向的值,而不是指標。
8. 小結
類除了定義該類型對象上的操作,還需要定義複製、賦值或撤銷該類型對象的含義。特殊成員函數(複製建構函式、賦值操作符和解構函式)可用於定義這些操作。這些操作統稱為“複製控制”函數。
如果類沒有定義這些操作中的一個或多個,編譯器將自動定義它們。合成操作執行逐個成員初始化、賦值或撤銷:合成操作依次取得每個成員,根據成員類型進行成員的複製、賦值或撤銷。如果成員為類類型的,合成操作調用該類的相應操作(即,複製建構函式調用成員的複製建構函式,解構函式調用成員的解構函式,等等)。如果成員為內建類型或指標,則直接複製或賦值,解構函式對撤銷內建類型或指標類型的成員沒有影響。如果成員為數組,則根據元素類型以適當方式複製、賦值或撤銷數組中的元素。
與複製建構函式和賦值操作符不同,無論類是否定義了自己的解構函式,都會建立和運行合成解構函式。
如果類定義了解構函式,則在類定義的解構函式結束之後運行合成解構函式。
定義複製控制函數最為困難的部分通常在於認識到它們的必要性。
分配記憶體或其他資源的類幾乎總是需要定義複製控製成員來管理所分配的資源。如果一個類需要解構函式,則它幾乎也總是需要定義複製建構函式和賦值操作符。
c語言操作符是什
你可以把操作符理解為內建的,最基礎的函數,它們無法完全被若干個未使用同類型操作符的函數所替代。比如加法運算子,你就不可能寫出一個不用+或-的函數來實現任何情況下的加法功能。
運算子和函數還有一個重要區別。函數本身有一段代碼,程式執行時,遇到函數時,會先將函數的參數入棧,再跳到函數的代碼來運行。而操作符則是在本地直接運算。
比如
#include<stdio.h>
#include<string.h>
int add(int a,int b)
{return a+b;
}
void main()
{
int a,b;
b=a+1;
b=add(a,1);
}
編譯後,查看彙編碼如下:
11: b=a+1;
0040D458 mov eax,dword ptr [ebp-4] //取a的值
0040D45B add eax,1 //加上1
0040D45E mov dword ptr [ebp-8],eax //和賦給b
12: b=add(a,1);
0040D461 push 1 //參數1入棧
0040D463 mov ecx,dword ptr [ebp-4] //將參數a移到寄存器
0040D466 push ecx //參數a入棧
0040D467 call @ILT+0(add) (00401005) //調用函數add
0040D46C add esp,8 //釋放參數佔用的記憶體
0040D46F mov dword ptr [ebp-8],eax //結果賦給b
明顯看出函數的調用過程比運算子要複雜。
C語言逗號操作符問題?
不少C++ newbie都會問到這問題,大家對+ - * / 自然是認識的了,對其他的%!& |之類的也不覺得陌生,但是逗號操作符?可能有一半的人會說不清楚它到底是幹啥的。
其實,我們是經常會用到逗號操作符的,但是並不是所有代碼裡出現的逗號都是逗號操作符。
讓我們先從一個類的範例程式碼開始
1:
2: class mynum
3: {
4: public:
5: mynum(double ndb,...)
6: {
7: //init with arguments
8: }
9: };
10: class someclass
11: {
12: someclass():
13: num(3,4)
14: ,a(0)
15: ,b(0)
16: ,c(15)
17: {
18: int i,j;
19: i=1,2; //int x=1,2;
20: for(;b<10;++b,--c)
21: {
22: ++a;
23: }
24: }
25: mynum num;
26: int a;
27: int b,c;
28: };
在這段代碼裡,逗號在5,13-16,18-20,27行都出現了。
現在先讓我們來看看逗號操作符是啥意思
然後我們看看上面的代碼
第5行是在mynum類的建構函式裡出現的逗號,只是用來區分開第一個double類型的參數和後面的可變參數。這是一個特例,如果在構造mynum對象的時候,本來是要傳入3.4作為第一個參數的,結果不小心把小數點變成了逗號的話,3.4就變成了兩個參數3和4——比如在第13行的情況。類似這樣的情況編譯器不會報錯,程式也能運行,但是mynum類裡的成員變數可能就因為建構函式傳入的變數值變化而導致程式運行結果完全不一致了。這兩個逗號都不是逗號操作符了。
第14,15,16行的逗號,也不是逗號操作符。這三個逗號只是用來間隔建構函式的成員變數初始化列表。
第18行的int i,j;是大家都很常用的變數聲明的語句,逗號在這裡,也只是起了一個間隔變數聲明的作用,也不是逗號運算子。
第19行的逗號,如果不認識逗號操作符的話,一定會認為這肯定是寫錯了!其實這正是逗號操作符了。讓我們看看,經過i=1,2;這個語句後,i 的值應該是多少呢?按照之前逗號操作符的定義,我們好像可以很輕鬆的得出i=2.然而,這個答案是錯的!你在得出錯誤答案的時候,忽略了一個重要的因素——運算子優先順序!由於=號的優先順序更高,所以i=1,2;這個語句相當於(i=1),2; ,如果要得到期望的2,我們應該這樣來寫i=(1,2);。下面的代碼可以驗證這個說法的(注意第3行是無意義代碼,但是可不是錯誤碼哦!)
1: int i,j;
2: i=1,2;
3: 3,4;
4: j=(1,2);
5: printf("%d“n%d“n",i,j);
繼續看第一份代碼裡的第20行,這是一個for迴圈代碼。這裡的逗號,也是逗號操作符哦。其實for迴圈也是逗號操作符經常出現的地方哦。for迴圈裡只能寫一個運算式,而逗號運算式這個時候就可以讓你完成兩個甚至多個運算式的計算。比如“for(;b<10;++b,——c)”,++b和——c就會在每次迴圈......餘下全文>>