c++回爐-類

//構造函數
Stock food = Stock("World Cabbage", 250, 1.2);    //顯式普通構造函數:寫出了函數名;Stock(....)是臨時對象,然後複製給了food.
Stock garment("Furry Mason", 50, 5.5);  //隱式普通構造函數:沒有函數名
Stock second();    //這是一個函數, 爲了和函數區分,隱式調用默認構造函數時候不加()
Stock second;   //對象,隱式調用默認構造函數
Stock second = Stock();   //對象,顯式調用默認構造函數
Stock* second = new Stock;   //對象,調用默認構造函數
Stock* second =  new Stock();   //對象,顯式調用默認構造函數
//初始化和賦值
Stock stock2 = Stock("xxxx", 2, 2.0);   //是初始化,可能會創建臨時對象,也可能不創建。
stock1 = Stock("yyy", 4, 2.5);   //是賦值,Stock(...)創建臨時變量,然後賦值,然後刪除臨時變量

SS  ss = 8;   //SS有一個構造函數,其參數只有一個, 
SS  ss;   ss = 8;   //涉及:用SS(int)創建臨時對象,把8作爲初始化值,逐成員賦值形式把臨時對象的內容複製到ss中。
ss=SS(8);   ss=(SS)8;   //用explicit禁止後,前一個也不能叫轉換,可以叫顯式調用構造函數賦值。後一個是強制類型轉換

const Stock& get_big(const Stock& s) const {
  if (s.data > data)   return s;
  else  return ????;    //這就是this指針的作用,   *this
}

Stock  arr[4];   //調用的默認構造函數
Stock arr[4] = {Stock(), Stock(), Stock(), Stock()};  //顯式調用一般構造函數

//enum
enum egg{small, big};		egg mine = big;    //big可能衝突,  sizeof(egg) = 4; 用int來表示enum值
enum class shirt(small, big); 	shirt clth = shirt::small;   //防止其他enum中也有small, 衝突
~~enum class : short shirt{xx, ml};    //xx, ml用short類型實現~~  錯了
enum shirt: short {xx, ml};   //sizeof(shirt) = 2

ostream& operator<<(ostream& os, const Time& time){
  os << time.h << time.m << "\n";    return os;
}   //可以級聯,  cout<<t1<<t2;
void  operator<<(ostream& os, const Time& time){
  os<<time.h << time.m << "\n";
}  //只能使用一次:   cout << t1;   等價於:cout--os,   t1--time

//轉換函數
operator double() const {return xx;}    Stone wolf;   //定義,必須是1類成員函數2沒有返回類型3不能有參數
double host = double(wolf);     //顯式調用1
double host = (double) wolf;     //顯式調用2
double host = wolf;      //隱式調用
explict operator double() const {return xx;};    double x=wolf;   //出錯,必須double x = double(wolf);或者double x=(double)wolf;

//static成員變量,作用於屬於類,但是生命週期是程序運行時間
class te{private: static int num;}     //聲明寫在類裏面,類寫在頭文件中
int te::num = 0;   //static成員變量必須在類外初始化,寫在實現文件中,如果寫在頭文件中,會重複定義error. const int可以在類內初始化,~~靜態枚舉也可以類內初始化~~ 。錯的,enum不能用static

struct LTD{
LTD(const LTD& des) = delete;    //用delete顯式禁止編譯器生成默認拷貝構造函數
void operator=(const LTD& des) = delete;   //禁止,這個運算符重載
}

char* p = new char;			  delete p;
char* p = new char[len];    delete [] p;
stringbad* pp = new stringbad(bad1);   //用bad1來初始化了新new的stringbad, 利用了拷貝構造函數,如果沒有自定義,使用默認的拷貝構造函數
stringbad* pp2 = new stringbad("ddss");  //調用對應構造函數初始化新創建的對象
stringbad* pp3 = new stringbad; //調用默認構造函數

//構造函數中有new動作時候,函數按值傳遞還是按引用傳遞都會受影響
struct stringbad {
	stringbad(const char* p);		stringbad();   //必須給一個默認構造函數,構造函數中有new的操作
	~stringbad();		//因爲有new,析構函數必須有,且有delete的操作
}
void callme1(stringbad & temp);     void callme2(stringbad temp);     //按引用和按值傳遞
callme1(obj1);	  //沒問題
callme2(obj2);   //出問題,值傳遞,複製了指針的值即是同一個地址, obj2-複製-temp-函數結束,temp釋放,調用析構函數,釋放了同一塊內存的東西,導致obj2中的數據受損
//對於局部變量,跳出scope時候自動調用析構函數,刪除的順序與創建局部變量的順序相反
//除了自己定義的構造函數,在參數傳遞,stringbad te2 = te1;這種情況時候都會調用拷貝構造函數,拷貝構造函數的形參是const stringbad&, 複製構造函數會創建一個副本,也就是stringbad te2 = stringbad(te1)
//參數傳遞初始化析構了一次,實參在函數結束又析構了一次,兩次釋放同一塊內存導致終止程序運行。

stringbad& string::operator=(const string& other){
  if (this == &other)  return *this;   //this是地址, *this是對象
  delete[]  str;    //如果沒有上面的判斷,在obj1=obj1完成之前,obj1裏面的東西已經空了
  len = other.len;     str=new char[len+1];   std::strcpy(str, other.str);   return *this;
}

struct string{
   string(const char*);
   string(const string&);
   string& operator=(const string&);
};
string te;		char* ch[40];   cin.getline(ch, 40);  
te = ch;    //這時候沒有operator=(const char*)   所以採用的是:string(ch)-->te=string(ch)--> ~string(ch), 但是如果重載了operator=(const char*)就不用創建刪除臨時對象了

類的public函數就是接口。
封裝:
1.public接口和實現細節分開,數據作爲private,不能直接訪問就是封裝
2.類函數定義和類聲明放在不同的文件中,定義編譯成lib不可見也是封裝
內聯函數應該放到頭文件中,被使用內聯函數的文件include。
類創建的每個新對象都有自己的存儲空間,用於存儲內部變量和類成員;同一個類的所有對象共享同一組類方法。
通過成員函數訪問數據成員,需要有一個函數負責數據成員的初始化–構造函數。
聲明類對象時候自動調用構造函數。
1.顯式調用構造函數
2.隱式調用構造函數
構造函數的形式:用於初始化數據成員。一般構造函數,傳遞參數過去;默認構造函數不傳遞參數。默認構造函數:1有形參,在形參階段提供默認值,2沒形參,在函數內部提供默認值。 給一般構造函數的參數提供默認值,相當於一舉兩得。
析構函數用於delete構造函數中new的東西,如果構造函數沒有new則析構函數相當於沒幹事。什麼時候寫析構函數,看構造函數有沒有申請內存空間。
什麼時候調用類對象的析構函數?
1.如果是static類型的,程序結束時候自動調用析構函數
2.如果是局部類型的,程序塊結束時候調用析構函數
3.如果是new出來的,使用delete時候調用析構函數
4.如果是臨時的,使用完後調用析構函數
類對象的賦值, 每個數據成員的內容進行賦值。
const對象意味着所有數據成員都是const,在調用成員函數時候,不能調用非const成員函數,因爲可能會改變數據成員。
如果一個類的構造函數只有一個參數,可以用賦值語法創建對象並初始化。這也可以看成是類的默認類型轉換。 explcit可以禁止這種特性。也就是explicit作爲單個參數的構造函數的前綴。
作用域爲類的常量: 枚舉,static; 所有對象都不包含枚舉,在類作用域中碰到枚舉就用枚舉值替換。這兩個都是因爲類沒有實例化,不能在類中開闢內存。
友元(友元函數,友元類,友元成員函數)和public函數可以操作private成員變量。
友元函數在類中用friend聲明,但不是類的成員函數。不需要::調用也不需要.而是operator+(x,y)這樣。
<< 重載必須用友元的原因:cout<<Time; 必須要用兩個對象來調用<<, 如果是成員函數則: Time << xxx; 這種調用顯然有問題。
運算符重載函數也是可以重載的,只要標識符不一樣(個數,類型,const)
類裏面有一種叫轉換函數的東西, 也叫運算符函數,用於強制類型轉換。因爲隱式可能是因爲誤操作,所以加explicit前綴,使轉換函數必須顯式調用。
類裏面的特殊成員函數:
1.默認構造函數,沒有定義任何構造函數時候生效
2.默認析構函數,沒有定義析構函數時候生效
3.拷貝構造函數,沒有定義拷貝構造函數時候特定調用形式下生效
4.賦值運算符,如果沒有重載的話,類會默認生效這個
5.地址運算符,沒有定義時候類默認生效
6.移動構造函數
7.移動複製運算符
1.1.默認構造函數構造的對象的數據成員是未知的,只能有一種形式的默認構造函數,如果有多種,編譯器不知道匹配哪個報錯
2.2.用於初始化時候,不是賦值時候。下面都是初始化操作都會調用拷貝構造函數。
stringbad ditto(mott);
stringbad mee = mott; //可能用拷貝,也可能用賦值
stringbad eg = stringbad(mott); //同上
stringbad* sg = new stringbad(mott)
void fun(stringbad xx); fun(mott);
逐個賦值非靜態變量的值,指針就是charp; ditto.p=mott.p; 按值傳遞; ditto.obj=mott.obj 調用obj的拷貝構造函數;
這也是淺拷貝(默認拷貝構造函數)的弊端,在有初始化操作時候,會多釋放一次同一塊內存,所以要自己定義拷貝構造函數,初始化時候,重新開闢空間並複製之前的內容到新開闢的空間。這樣在初始化使用完後就不會析構掉原始數據的內存了,析構的是被初始化對象自己的內存。
如果一個類包含了用new來初始化的指針對象時候,就要顯式定義一個拷貝構造函數。
4.4.默認的賦值運算符原型是:
stringbad& stringbad::operator=(const stringbad&); 接收一個對象引用,返回一個對象引用
賦值和初始化的一個區分就是,初始化是對沒有初始值的變量進行的操作,賦值時對已經有初始值的變量進行的操作。比如:
stringbad ob1(“xxxxxx”);
stringbad obj2; obj2 = obj1; //就是賦值
爲什麼賦值運算符重載時候不能賦值給自身就是上面這個問題,obj1 = obj1,在數據複製給左邊的obj1之前,右邊的obj1裏面的數據就被析構掉了。必須返回引用, 可以級聯。代碼如上。
c++11的空指針: nullptr. int
p = nullptr.
靜態成員函數:
1.不能通過對象調用靜態成員函數
2.靜態成員函數沒有this指針,不與對象關聯,所以只能處理靜態成員變量,靜態成員函數,屬於類的成分
重載賦值運算符的好處是節省了臨時對象的構建和刪除。
構造函數有多個,但是析構函數只有一個,所以不同的構造函數要兼容析構函數。
返回對象,結合函數,一個類對象作爲函數的返回值:
1.返回對象時候如果不是引用,調用拷貝構造函數
2.如果返回的引用是來自於形參,則形參是const,返回值也得是const引用
3.按值返回時候(返回局部變量),使用拷貝構造函數生成返回的對象
4.返回指向對象的指針,

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章