css做購物網(wǎng)站的分類目錄搜索引擎優(yōu)化搜索優(yōu)化
一 拷貝構造函數(shù)的概念:
拷貝構造函數(shù)是一種特殊的構造函數(shù),用于創(chuàng)建一個對象是另一個對象的副本。當需要用一個已存在的對象來初始化一個新對象時,或者將對象傳遞給函數(shù)或從函數(shù)返回對象時,會調(diào)用拷貝構造函數(shù)。
二?拷貝構造函數(shù)的特點:
1:拷貝構造函數(shù)是構造函數(shù)的一個重載形式。
2:拷貝構造函數(shù)的參數(shù)只有一個且必須是類類型對象的引用,使用傳值方式編譯器直接報錯, 因為會引發(fā)無窮遞歸調(diào)用。
3:若未顯式定義,編譯器會生成默認的拷貝構造函數(shù)。 默認的拷貝構造函數(shù)對象按內(nèi)存存儲按 字節(jié)序完成拷貝,這種拷貝叫做淺拷貝,或者值拷貝。
注意:在編譯器生成的默認拷貝構造函數(shù)中,內(nèi)置類型是按照字節(jié)方式直接拷貝的,而自定 義類型是調(diào)用其拷貝構造函數(shù)完成拷貝的。
4:編譯器生成的默認拷貝構造函數(shù)已經(jīng)可以完成字節(jié)序的值拷貝了,還需要自己顯式實現(xiàn)嗎? 當然像日期類這樣的類是沒必要的。
2.1 代碼示例:
class Time
{
public:// 普通構造函數(shù)Time(int hour = 0, int minute = 0, int second = 0) {_hour = hour;_minute = minute;_second = second;}// 拷貝構造函數(shù),使用引用傳遞Time(const Time& other) {_hour = other._hour;_minute = other._minute;_second = other._second;}void Print() const {std::cout << _hour << ":" << _minute << ":" << _second << std::endl;}private:int _hour;int _minute;int _second;
};int main()
{Time t1(10, 20, 30); // 使用普通構造函數(shù)//構造函數(shù)的重載Time t2 = t1; // 使用拷貝構造函數(shù)//Time t2(t1); // 拷貝構造的另一種寫法t1.Print();t2.Print();return 0;
}
輸出:
2.2 為什么要使用引用呢?
我們在 increment 函數(shù)中改變x的值并沒有間接性改變a,這是因為傳過去的只是編譯器創(chuàng)建實參的一個副本,而修改副本怎么可能可以改變a呢?
#include <iostream>void increment(int x)
{x = x + 1; // 修改的是副本,不影響實參
}int main()
{int a = 5;increment(a); // 傳遞a的副本std::cout << a << std::endl; // 輸出5,原始值a未被修改return 0;
}
知道傳值傳參的本質之后,再來想一想為什么要用引用?咱們先來說說如果沒用用引用的后果會是怎么樣,當把自定義類型傳出去后且不用引用或者指針來接收,它會
調(diào)用 Time(const Time other)
,其中 other
是 t1
的按值傳遞副本。
為了按值傳遞,編譯器需要創(chuàng)建 other
的副本。
創(chuàng)建 other
的副本時,再次調(diào)用 Time(const Time other)
。
這個新調(diào)用的 Time(const Time other)
又需要創(chuàng)建自己的 other
副本,再次調(diào)用 Time(const Time other)
。
如此反復,導致無限遞歸調(diào)用,最終導致棧溢出。
圖:
C++規(guī)定,自定義類型的拷貝,都會調(diào)用拷貝構造
那為什么要引用呢?
首先我們來回顧一下引用 :
1:引用是現(xiàn)有變量的另一個名字。
2:它們不創(chuàng)建新對象,只是指向已有對象。
3:引用只是指向現(xiàn)有對象,不創(chuàng)建新副本
因為引用就是它本身,所以何來創(chuàng)建新副本這一說法,創(chuàng)建新副本是怕改變副本從而導致改變實參值
2.3 總結:
1:按值傳遞會遞歸:每次傳遞對象會復制對象,導致無限遞歸。
2:引用傳遞避免遞歸:引用只是指向對象本身,不會復制對象
三 默認拷貝構造:
當你沒有顯式定義拷貝構造函數(shù)時,編譯器會為你自動生成一個默認的拷貝構造函數(shù)。這個默認拷貝構造函數(shù)會逐個拷貝對象的所有成員變量。
3.1 內(nèi)置類型與自定義類型的拷貝:
內(nèi)置類型:如 int
, char
, float
等,拷貝時直接按照字節(jié)方式進行復制,也就是直接復制其值。
自定義類型:如類和結構體,拷貝時會調(diào)用該類型的拷貝構造函數(shù)。
3.2 代碼示例:
內(nèi)置類型:
#include <iostream>class MyClass
{
public:int x; // 內(nèi)置類型成員
};int main()
{MyClass obj1;obj1.x = 10;MyClass obj2 = obj1; // 使用編譯器生成的默認拷貝構造函數(shù)std::cout << "obj1.x: " << obj1.x << std::endl; std::cout << "obj2.x: " << obj2.x << std::endl;return 0;
}
輸出:
對于一個類里面只有內(nèi)置類型成員那編譯器生成的默認拷貝構造會自動復制其值。
自定義類型:
#include <iostream>class Time
{
public:// 默認構造函數(shù)Time() { _hour = 0;_minute = 0;_second = 0;}// 拷貝構造函數(shù)Time(const Time& other) {_hour = other._hour;_minute = other._minute;_second = other._second;std::cout << "Time::Time(const Time& other)" << std::endl;}private:int _hour;int _minute;int _second;
};class MyClass
{
public:int x; // 內(nèi)置類型成員Time t; // 自定義類型成員
};int main()
{MyClass obj1;obj1.x = 10;MyClass obj2 = obj1; // 使用編譯器生成的默認拷貝構造函數(shù)std::cout << "obj1.x: " << obj1.x << std::endl;std::cout << "obj2.x: " << obj2.x << std::endl; return 0;
}
當執(zhí)行MyClass obj2 = obj1; 因obj1類里面有自定義類型 t 所以編譯器生成的默認拷貝構造會自動調(diào)用Time(const Time& other) 來完成
3.3 總結:
內(nèi)置類型:編譯器默認拷貝構造函數(shù)會直接復制其值。
自定義類型:編譯器默認拷貝構造函數(shù)會調(diào)用該類型的拷貝構造函數(shù)來復制其內(nèi)容。
四 內(nèi)存分區(qū):
要理解好深拷貝與淺拷貝那就得先了解內(nèi)存是怎么樣分區(qū)的。
計算機程序運行時,內(nèi)存通常被分為四個主要區(qū)域:棧區(qū)、堆區(qū)、全局靜態(tài)區(qū)和只讀區(qū)(常量區(qū)和代碼區(qū))。
4.1 棧區(qū):
局部變量:函數(shù)內(nèi)部定義的變量。
形參(函數(shù)參數(shù)):函數(shù)定義時的參數(shù)。
返回地址:函數(shù)調(diào)用后的返回地址。
特點:
棧區(qū)中訪問速度快且棧的內(nèi)存連續(xù)分配。
因存儲的都是 局部/形參/返回地址 所以棧區(qū)空間小,存儲的生命周期短。
在我們局部變量所在的函數(shù)執(zhí)行完成時,它會自動釋放內(nèi)存。
4.2 堆區(qū):
動態(tài)分配的數(shù)據(jù):通過 new
或 malloc
等動態(tài)分配函數(shù)分配的內(nèi)存。
特點:
因存儲的都是new 或者malloc開辟的空間所以堆區(qū)空間大,所以訪問速度慢。
堆中的內(nèi)存分配和釋放是通過指針進行的,可能不是連續(xù)的。
堆區(qū)的內(nèi)存需要程序員手動管理,必須手動釋放動態(tài)分配的內(nèi)存,否則會導致內(nèi)存泄漏。
4.3 全區(qū)/靜態(tài)區(qū):
全局變量:在所有函數(shù)外部定義的變量。
靜態(tài)變量:使用 static
關鍵字定義的變量。
特點:
全局變量和靜態(tài)變量在程序的整個運行期間一直存在,直到程序結束。
全局變量可以在程序的所有函數(shù)中訪問,靜態(tài)變量在聲明的作用域內(nèi)共享。
4.4 只讀常量區(qū):
常量:程序中定義的常量。
代碼:程序的指令代碼。
特點:
常量區(qū)的數(shù)據(jù)在程序運行期間不能被修改,保證了數(shù)據(jù)的安全性和穩(wěn)定性。
代碼區(qū)存儲程序的指令代碼,在程序運行時被載入內(nèi)存以執(zhí)行。
五 淺拷貝:
首先我們來回顧C語言里面的基本類型和指針類型。
5.1 基本類型:
基本類型是C語言內(nèi)置的數(shù)據(jù)類型,它們用于存儲最基本的數(shù)值數(shù)據(jù)。常見的基本類型包括:int float char……
5.2 指針類型:
指針類型是存儲內(nèi)存地址的數(shù)據(jù)類型。指針用于指向其他變量或對象在內(nèi)存中的位置。
5.3 基本類型代碼示例:
#include <iostream>class BasicType
{
public:int value;// 構造函數(shù)BasicType(int v) {value = v;}// 拷貝構造函數(shù)BasicType(const BasicType& other) {value = other.value;}
};int main()
{BasicType obj1(10);BasicType obj2 = obj1; // 淺拷貝,復制基本類型的值std::cout << "改變前: " << std::endl;std::cout << "obj1.value: " << obj1.value << std::endl;std::cout << "obj2.value: " << obj2.value << std::endl;obj2.value = 20; // 修改obj2的值std::cout << "改變后: " << std::endl;std::cout << "obj1.value: " << obj1.value << std::endl;std::cout << "obj2.value: " << obj2.value << std::endl;return 0;
}
輸出:
值會被復制但修改新對象的值不會影響原對象。
5.3 指針類型代碼示例:
#include <iostream>class SimplePointer
{
public:int* ptr; // 成員變量 ptr// 構造函數(shù)SimplePointer(int value)
{ptr = (int*)malloc(sizeof(int)); // 動態(tài)分配內(nèi)存并初始化if (ptr != nullptr) {*ptr = value;}
}SimplePointer(const SimplePointer& other) {this->ptr = other.ptr; // 淺拷貝,復制內(nèi)存地址}void print() const {std::cout << "Value: " << *ptr << std::endl;}
};int main()
{SimplePointer obj1(10); // 創(chuàng)建第一個對象,并將值初始化為10SimplePointer obj2(obj1); // 使用拷貝構造函數(shù)(淺拷貝)// 打印初始值std::cout << "Initial values:" << std::endl;obj1.print();obj2.print();// 修改obj2的值*obj2.ptr = 20;// 打印修改后的值std::cout << "After change:" << std::endl;obj1.print();obj2.print(); return 0;
}
輸出:
復制內(nèi)存地址,共享同一塊內(nèi)存,修改會互相影響
六 深拷貝:
#include <iostream>
#include <cstdlib>
#include <cstring>class SimpleClass
{
public:int* ptr;// 默認構造函數(shù)SimpleClass(int value) {ptr = (int*)malloc(sizeof(int)); // 動態(tài)分配內(nèi)存并初始化if (ptr != nullptr) {*ptr = value;}}// 深拷貝構造函數(shù)SimpleClass(const SimpleClass& other) {ptr = (int*)malloc(sizeof(int)); // 分配新內(nèi)存if (ptr != nullptr) {*ptr = *(other.ptr); // 復制內(nèi)容}}// 析構函數(shù)~SimpleClass() {if (ptr != nullptr) {free(ptr); // 釋放內(nèi)存}}void Print() const {if (ptr != nullptr) {std::cout << "Value: " << *ptr << std::endl;}}
};int main()
{SimpleClass obj1(10); // 創(chuàng)建對象,ptr 指向的值為 10SimpleClass obj2 = obj1; // 使用深拷貝構造函數(shù)obj1.Print();obj2.Print();// 修改 obj2 的值if (obj2.ptr != nullptr) {*(obj2.ptr) = 20;}obj1.Print();obj2.Print();return 0;
}
輸出:
深拷貝不僅復制對象的指針成員,還為指針指向的內(nèi)容分配新的內(nèi)存,并復制原對象的數(shù)據(jù)。這樣,兩個對象擁有獨立的內(nèi)存,修改一個不會影響另一個。