拷貝構造函數(shù)的使用
-
在前幾章學習對象的時候,我們有的時候需要一個與已存在對象一某一樣的新對象
-
那在創(chuàng)建對象時,可否創(chuàng)建一個與已存在對象一某一樣的新對象呢?
-
拷貝構造函數(shù):只有單個形參,該形參是對本類類型對象的引用(一般常用const修飾),在用已存在的類類型對象創(chuàng)建新對象時由編譯器自動調用。
拷貝構造函數(shù)也是特殊的成員函數(shù),其特征如下:
- 拷貝構造函數(shù)是構造函數(shù)的一個重載形式。
- 拷貝構造函數(shù)的參數(shù)只有一個且必須是類類型對象的引用,使用傳值方式編譯器直接報錯,因為會引發(fā)無窮遞歸調用。
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
//Date(const Date d) // 錯誤寫法:編譯報錯,會引發(fā)無窮遞歸
Date(const Date& d) // 必須傳引用
{
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2024,2,1);
Date d2(d1);
return 0;
}
- 下面我們解釋一下:
- 這里的
const
可加可不加,但是建議加上 - 有些人就寫這個寫昏頭了,寫反了,不加const的話不會提示
- 加上了就知道哪里錯了,所以還是加上較好
- 這里的拷貝構造必須傳引用,要不然會引發(fā)無窮遞歸【反正編譯會報錯~】
- 再看下面代碼,我是沒有寫拷貝構造的,但是這里自動拷貝了,畢竟這個是默認成員函數(shù),這里生成的還和之前幾個的默認成員函數(shù)還不一樣,之前的對默認成員函數(shù)對內置類型不處理,而這個拷貝構造對內置類型處理了,如果沒有處理,這里就拷貝不出來
- 說明這里會自動生成一個拷貝構造函數(shù),將值拷貝回去
- 若未顯式定義,編譯器會生成默認的拷貝構造函數(shù)。 默認的拷貝構造函數(shù)對象按內存存儲按字節(jié)序完成拷貝,這種拷貝叫做淺拷貝,或者值拷貝。
class Date
{
public:
// 構造函數(shù)
Date(int year = 2024, int month = 2, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2024, 2, 1);
Date d2(d1);
d1.Print();
d2.Print();
return 0;
}
拷貝構造對于自定義類型【淺拷貝】
- 那么對于自定義類型呢?
class Time
{
public:
Time(const Time& t)
{
_hour = t._hour;
_minute = t._minute;
_second = t._second;
cout << "Time::Time(const Time&)" << endl;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
Date(int year = 2024, int month = 2, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
// 自定義類型
Time _t;
};
int main()
{
Date d1(2024,2,1);
Date d2(d1);
d1.Print();
d2.Print();
return 0;
}
-
我們有一個方法就是強制讓編譯器生成
-
加上這一條:
Time() = default;
深拷貝
-
剛剛上面的一種場景叫做淺拷貝,還有一個場景就是深拷貝
-
編譯器生成的默認拷貝構造函數(shù)已經(jīng)可以完成字節(jié)序的值拷貝了,還需要自己顯式實現(xiàn)嗎?當然像日期類這樣的類是沒必要的。那么下面的類呢?驗證一下試試?
-
下面我這個代碼沒有寫拷貝構造,它會自動生成一個默認的值拷貝,我們來運行一下
typedef int DataType;
class Stack
{
public:
Stack(size_t capacity = 10)
{
_array = (DataType*)malloc(capacity * sizeof(DataType));
if (nullptr == _array)
{
perror("malloc申請空間失敗");
return;
}
_size = 0;
_capacity = capacity;
}
void Push(const DataType& data)
{
// CheckCapacity();
_array[_size] = data;
_size++;
}
~Stack()
{
if (_array)
{
free(_array);
_array = nullptr;
_capacity = 0;
_size = 0;
}
}
private:
DataType* _array;
size_t _size;
size_t _capacity;
};
int main()
{
Stack s1;
s1.Push(1);
s1.Push(2);
s1.Push(3);
s1.Push(4);
Stack s2(s1);
return 0;
}
- 我們看到程序崩潰了~~
-
也就是在一些場景下,默認生成的拷貝構造是會出事的
-
再調試看一下:
- 這會導致兩個對象指向同一塊空間
- 在結束的時候會調用析構函數(shù),會把那塊空間給釋放了
- 再次釋放的時候會出現(xiàn)錯誤
- 同一塊空間被釋放了兩次【所以是絕對不能的】
- 正確的我們這樣寫
- 需要引入一個叫做深拷貝
Stack(const Stack& s)
{
// 深拷貝
DataType* tmp = (DataType*)malloc(s._capacity * sizeof(DataType));
if (nullptr == tmp)
{
perror("malloc fail\n");
exit(-1);
}
memcpy(tmp, s._array, sizeof(DataType) * s._size);
_array = tmp;
// 淺拷貝
_size = s._size;
_capacity = s._capacity;
}
注意:類中如果沒有涉及資源申請時,拷貝構造函數(shù)是否寫都可以;一旦涉及到資源申請時,則拷貝構造函數(shù)是一定要寫的,否則就是淺拷貝
拷貝構造函數(shù)典型調用場景
- 使用已存在對象創(chuàng)建新對象
- 函數(shù)參數(shù)類型為類類型對象
- 函數(shù)返回值類型為類類型對象
class Date
{
public:
Date(int year, int minute, int day)
{
cout << "Date(int,int,int):" << this << endl;
}
Date(const Date& d)
{
cout << "Date(const Date& d):" << this << endl;
}
~Date()
{
cout << "~Date():" << this << endl;
}
private:
int _year;
int _month;
int _day;
};
Date Test(Date d)
{
Date temp(d);
return temp;
}
int main()
{
Date d1(2022, 1, 13);
Test(d1);
return 0;
}
文章來源:http://www.zghlxwxcb.cn/news/detail-830246.html
- 為了提高程序效率,一般對象傳參時,盡量使用引用類型,返回時根據(jù)實際場景,能用引用盡量使用引用。
最后本文就到這里結束了,感謝大家的收看,請多多指點~文章來源地址http://www.zghlxwxcb.cn/news/detail-830246.html
到了這里,關于C++中類的6個默認成員函數(shù) 【拷貝構造函數(shù)】的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!