C++拷貝構造器、運算符重載1
#include <iostream>
using namespace std;
class Str //模仿string類
{
????public:
???? Str(const char* ptr="") //接收字符串來初始化對象,不修改參數所以const,用默認參數來兼容無參構造器(無參生成空字符串)
???? {
???????? arr = new char[strlen(ptr) + 1]; //創(chuàng)建和參數長度一樣大的空間,動態(tài)分配的內存位于堆中
???????? strcpy(arr, ptr); //字符串賦值
???? }
???? ~Str()
???? {
???? ????delete[] arr; //釋放堆中的數組
???? }
???? Str(const Str& another) //拷貝構造器,使用一個已存在的對象初始化新的對象,默認生成,會將形參的所有字段拷貝用來初始化,格式固定: 類名(const 類名& 引用名),使用了引用,another就是主調函數中的實參對象本身
???? {
???????? //如果不手動定義拷貝構造器,默認生成的拷貝構造器會對所有字段進行成員賦值(淺拷貝),執(zhí)行arr=another.arr,這對基礎類型以及結構不會產生問題,但指針賦值會使兩個對象使用同一塊動態(tài)內存空間
???????? arr = new char[strlen(another.arr) + 1]; //在類的函數中不僅可以調用當前對象的私有字段,也可以調用其他同類型對象的私有字段,對象中有指針字段時需要重寫拷貝構造器
???????? strcpy(this->arr, another.arr); //過程和使用字符串初始化類似,this是指向當前對象的指針,*this就是當前Str對象(的別名),直接調用arr就相當于(*this).arr
???????? cout << "copy" << endl;
???? }
???? Str& operator=(const Str& another) //賦值運算符重載,將一個存在的對象賦值給一個存在的對象,默認生成,和默認的拷貝構造器一樣執(zhí)行成員賦值,只拷貝字段不創(chuàng)建額外生成的動態(tài)內存空間,固定格式: 類名& operator=(const 類名& 引用名),返回當前對象的引用
???? {
???????? if (this == &another) //this為指向當前對象的指針,another為另一個對象的引用即另一個Str本身,判斷當前對象的地址(this的值)和另一個對象的地址(&another)相同即自己賦值自己,直接返回
???????? ???? return *this; //*this為當前對象,將當前對象的引用返回,不能返回another因為聲明const
???????? delete[]arr; //先釋放已創(chuàng)建的內存
???????? arr = new char[strlen(another.arr) + 1];
???????? strcpy(this->arr, another.arr); //和上面的構造方法相同
???????? cout << "overload" << endl;
???? }
???? //構造器、析構器、拷貝構造器、賦值運算符重載、地址運算符重載(C++11增加移動構造器、移動賦值運算符重載),這五個函數在沒有手動定義的情況下會默認生成;默認的地址運算符重載會返回對象所在的地址
???? const char* c_str()
???? {
???? ????return arr; //將私有的數組指針返回并聲明const禁止修改
???? }
???? size_t find(char ch)
???? {
???????? char* ptr = strchr(arr, ch);
???????? return ptr ? ptr - arr : (size_t)-1;
???? }
???? Str operator+(const Str& another) //模擬字符串相加,s1+s2返回一個新的字符串,內容為s1拼接s2
???? {
???????? Str res; //生成新的對象
???????? res.~Str(); //釋放無參構造器生成的char[1]數組
???????? res.arr = new char[strlen(this->arr)+strlen(another.arr) + 1]; //兩個對象字符串長度之和,留一個\0
???????? strcpy(res.arr, this->arr); //先拷貝this
???????? strcat(res.arr, another.arr); //使用strcat拼接
???????? return res; //res對象是自動變量,在返回后釋放,所以不能返回res的引用,返回操作實際執(zhí)行了一次拷貝構造器,在函數外(具體位置取決于實現)用res初始化了一個臨時空間交給主調函數
???? }
???? bool operator>(const Str& another) //通過strcmp實現對象的比較
???? {
???? ????return strcmp(arr, another.arr) > 0;
???? }
???? bool operator<(const Str& another)
???? {
???? ????return strcmp(arr, another.arr) < 0;
???? }
???? bool operator==(const Str& another) //三種運算符分別重載
???? {
???? ????return strcmp(arr, another.arr) == 0;
???? }
???? char& operator[](int index) //實現對象s1[i]取元素
???? {
???????? return arr[index]; //返回元素本身可修改,就像char數組[i]一樣使用
????????//注意這里的返回類型為char&,如果是char則返回字符的備份,備份無法修改不能改變下標的字符,
???? }
???? char operator[](int index)const //const成員函數,如果調用對象為const時就不能返回下標字符本身因為可能會被修改,所以去掉&變成按值傳遞,返回char的值/下標字符的備份,格式為:函數頭 const {函數體},后續(xù)詳細介紹
???? {
???? ????return arr[index]; //返回的內容沒有變化,區(qū)別只在返回值和const
???? }
????private:
???? char* arr;
};
int mainclass2()
{
???? Str s,s1("s1"),s2("s2");
???? cout << s1.c_str() << endl;
???? cout << s1.find('1') << endl;
???? Str s3(s1); //拷貝構造器
???? Str s4 = s2; //也是拷貝構造器,聲明階段初始化值,和賦值不同
???? cout << s4.c_str() << endl;
???? Str s5;
???? s5 = s3; //賦值,相當于s5.operator=(s3)
???? s1 + s2; //s1+s2相當于s1.operator+(s2),返回結果是匿名空間,在c++代碼層面無法訪問這個沒有標識符的空間
???? Str s6 = s1 + s2;//將返回的空間標識為s6,所以過程中發(fā)生了一次無參構造、一次拷貝構造
???? cout << s6.c_str() << endl;
???? Str s7;
???? s7 = s3 + s4; //相當于s7.operator=(s3.operator+(s4)),運算符重載遵循運算符的優(yōu)先級
???? Str sarr[10] = { Str("ele1"),Str("ele2") }; //對象數組,和普通數組初始化一樣用{},如果初始化的項數小于數組大小,剩下的元素會調用無參構造器,所以聲明對象時應保留無參構造器
???? return 0;
}
Str returnclass()
{
???? return Str("匿名對象"); //先通過有參構造器創(chuàng)建對象,在函數return該對象時就會發(fā)生拷貝構造,將返回的對象作為拷貝構造器的參數,在臨時空間使用拷貝構造創(chuàng)建臨時對象,之后被調函數結束,自動變量釋放,主調函數訪問臨時空間
???? //和返回基本數據類型同理,return 1是返回1的備份,return 對象 是返回對象的備份,通過拷貝構造器創(chuàng)建備份
}
class ClassNoCopy
{
????public:
???? ClassNoCopy() {}
????private:
???? ClassNoCopy(const ClassNoCopy& another) {} //將拷貝構造器私有,禁止外部調用,手動聲明后不會再默認生成公有的拷貝構造器
???? ClassNoCopy& operator=(const ClassNoCopy& another)?= delete; //使用delete關鍵字禁用該成員函數,格式:任意成員函數的函數頭 = delete,當主調函數調用這個成員函數時會報錯
???? int val;
};
ClassNoCopy funcCNC()
{
ClassNoCopy c1;
//報錯:ClassNoCopy c2(c1); //拷貝構造器私有不可訪問
//報錯:ClassNoCopy c3 = c1; //聲明時初始化同樣調用拷貝構造器,私有不可訪問
ClassNoCopy c4;
//報錯:c4 = c1; //將一個對象賦值給另一個對象,賦值運算符重載不可訪問
//報錯:return c1; //返回對象調用拷貝構造器,私有不可訪問
}
class ClassOperatorOverload
{
????public:
???? int data;
???? ClassOperatorOverload(int val = 0)
???? {
???? ????data = val;
???? }
???? //ClassOperatorOverload& operator=(const ClassOperatorOverload& another)
???? //{
???????? // /*省略自己賦值自己的判斷*/
???????? // data = another.data;
???????? // return *this; //返回當前對象本身,可以實現鏈式賦值 c1 = c2 = c3 將c3賦值給c2后返回c2,再將c2賦值給c1(等號結合律右到左)。也支持(c1 =c2)=c3 將c2賦給c1返回c1,再將c3賦給c1返回c1
???? //}
???? //const ClassOperatorOverload& operator=(const ClassOperatorOverload& another) //將返回聲明為const,禁止將返回值作為左值,表達式 c1 = c2 = c3 仍然正常執(zhí)行(=運算符從右向左執(zhí)行),但(c1=c2)=c3報錯,因為c1=c2返回的c1的引用為const引用
???? //{
???????? // /*省略自己賦值自己的判斷*/
???????? // data = another.data;
???????? // return *this;
???? //}
???? void operator=(const ClassOperatorOverload& another) //不返回對象,這時c1=c2正常,c1=c2=c3報錯因為c2=c3無返回,而只改變返回值類型不構成重載,但都視為賦值運算符的函數,不再生成默認的
???? {
???????? // /*省略自己賦值自己的判斷*/
???????? data = another.data;
???? }
};