อะไรคือความแตกต่างระหว่างตัวดำเนินการกำหนดและตัวสร้างการคัดลอก?


107

ฉันไม่เข้าใจความแตกต่างระหว่างตัวสร้างการกำหนดและตัวสร้างการคัดลอกใน C ++ มันเป็นเช่นนี้:

class A {
public:
    A() {
        cout << "A::A()" << endl;
    }
};

// The copy constructor
A a = b;

// The assignment constructor
A c;
c = a;

// Is it right?

ฉันต้องการทราบวิธีการจัดสรรหน่วยความจำของตัวสร้างการกำหนดและตัวสร้างการคัดลอก?


2
คุณมีหนังสือ C ++ ที่ดีหรือไม่?
sbi

คำตอบ:


161

ตัวสร้างการคัดลอกใช้เพื่อเริ่มต้นอ็อบเจ็กต์ที่ไม่ได้เริ่มต้นก่อนหน้านี้จากข้อมูลของอ็อบเจ็กต์อื่น

A(const A& rhs) : data_(rhs.data_) {}

ตัวอย่างเช่น:

A aa;
A a = aa;  //copy constructor

ผู้ประกอบการที่ได้รับมอบหมายจะใช้ในการแทนที่ข้อมูลของการเริ่มต้นก่อนหน้านี้วัตถุที่มีข้อมูลบางส่วนวัตถุอื่น ๆ

A& operator=(const A& rhs) {data_ = rhs.data_; return *this;}

ตัวอย่างเช่น:

A aa;
A a;
a = aa;  // assignment operator

คุณสามารถแทนที่การสร้างสำเนาด้วยโครงสร้างเริ่มต้นบวกการกำหนด แต่จะมีประสิทธิภาพน้อยกว่า

(หมายเหตุด้านข้าง: การใช้งานของฉันข้างต้นเป็นสิ่งที่คอมไพเลอร์มอบให้คุณฟรีดังนั้นจึงไม่สมเหตุสมผลที่จะใช้งานด้วยตนเองหากคุณมีหนึ่งในสองอย่างนี้เป็นไปได้ว่าคุณกำลังจัดการทรัพยากรบางอย่างด้วยตนเอง ในกรณีนั้นตามกฎสามข้อคุณอาจต้องใช้อีกอันหนึ่งบวกกับตัวทำลายด้วย)


4
หมายเหตุ: ปัจจุบัน (C ++ 11 เป็นต้นไป) พวกเขาสามารถผิดนัดอย่างชัดเจนด้วย=default;.
Deduplicator

2
@Deduplicator สิ่งสำคัญคือต้องพูดถึงว่าเมื่อยึดมั่นในการจำแนกประเภทที่ต้องใช้ตัวสร้างเล็กน้อยคุณจะต้องมีสิ่ง = defaultเหล่านี้ในกรณีที่จำเป็นต้องใช้ ctor เริ่มต้น: เพียงแค่ใช้ตัวว่างเปล่าด้วยตัวเราเองยังคงนับเป็น ctor ที่ผู้ใช้กำหนดและดังนั้น (ในระดับมาตรฐาน ) ไม่สำคัญและตัดสิทธิ์ประเภทจากการจัดประเภทที่ต้องใช้ ctor เล็กน้อย
underscore_d

@sbi ฉันสามารถพูดได้ไหมว่าในกรณีที่ไม่ได้ใช้ตัวสร้างการคัดลอกและใช้ตัวดำเนินการกำหนดแทนอ็อบเจ็กต์จะถูกสร้างขึ้นก่อนโดยเรียกตัวสร้างโดยมีอาร์กิวเมนต์หรือไม่มีอาร์กิวเมนต์จากนั้นจะใช้ตัวดำเนินการกำหนดค่าและค่าใหม่จะถูกกำหนดตาม RHS ในกรณีที่ใช้ตัวสร้างการคัดลอกจะยังคงเรียกตัวสร้างเหมือนเดิม แต่ค่าที่ใช้ในการเริ่มต้นมาจากวัตถุอื่น
Rajesh

@Rajesh: ฉันสับสนเกี่ยวกับสิ่งที่คุณถามและความรู้สึกของฉันก็เป็นเพราะคุณก็สับสนเช่นกัน :)คุณจะลองอีกครั้งเพื่ออธิบายสิ่งที่คุณกำลังพูดถึงหรือไม่?
sbi

1
@ CătălinaSîrbu: คุณทำได้ เป็นฟังก์ชันอิสระสองฟังก์ชัน
sbi

42

ความแตกต่างระหว่างตัวสร้างการคัดลอกและตัวดำเนินการกำหนดทำให้เกิดความสับสนอย่างมากสำหรับโปรแกรมเมอร์มือใหม่ แต่จริงๆแล้วมันก็ไม่ใช่เรื่องยากทั้งหมด สรุป:

  • หากต้องสร้างออบเจ็กต์ใหม่ก่อนที่จะเกิดการคัดลอกได้จะใช้ตัวสร้างการคัดลอก
  • หากไม่จำเป็นต้องสร้างออบเจ็กต์ใหม่ก่อนที่จะเกิดการคัดลอกได้จะใช้ตัวดำเนินการกำหนด

ตัวอย่างสำหรับตัวดำเนินการกำหนด:

Base obj1(5); //calls Base class constructor
Base obj2; //calls Base class default constructor
obj2 = obj1; //calls assignment operator

ตัวอย่างสำหรับตัวสร้างการคัดลอก:

Base obj1(5);
Base obj2 = obj1; //calls copy constructor

จะยุติธรรมหรือไม่ที่จะบอกว่าผู้ดำเนินการมอบหมายจะรวมการทำลายวัตถุเก่าเข้ากับการสร้างวัตถุใหม่ได้อย่างมีประสิทธิภาพ แต่มีข้อบัญญัติว่า (1) หากขั้นตอนใดขั้นตอนหนึ่งในการทำลายวัตถุเก่าจะถูกยกเลิกโดย ขั้นตอนใดขั้นตอนหนึ่งในการสร้างขั้นตอนใหม่ทั้งสองขั้นตอนอาจถูกละเว้น (2) ตัวดำเนินการกำหนดไม่ควรทำสิ่งที่ไม่ดีหากมีการกำหนดวัตถุให้กับตัวมันเอง
supercat

ทำไมต้องทำvector <A> v3แล้วv3 = v2 ( v2องค์ประกอบที่ประกาศและมีไว้ก่อนหน้านี้อยู่ที่ไหนvector<A>)เรียกตัวAสร้างการคัดลอกที่ชัดเจนของฉันแทนoperator=? ฉันคาดว่าoperator=จะถูกเรียกแทนcopy constructorเนื่องจากv3วัตถุของฉันได้รับการประกาศแล้วในเวลาที่ฉันทำงาน
CătălinaSîrbu

19

ประการแรกคือการเริ่มต้นการคัดลอกส่วนที่สองเป็นเพียงการกำหนด ไม่มีสิ่งที่เรียกว่าตัวสร้างการมอบหมาย

A aa=bb;

ใช้ตัวสร้างสำเนาที่สร้างโดยคอมไพเลอร์

A cc;
cc=aa;

ใช้ตัวสร้างเริ่มต้นเพื่อสร้างccจากนั้นตัวดำเนินการ * การกำหนด ** ( operator =) บนวัตถุที่มีอยู่แล้ว

ฉันต้องการทราบวิธีจัดสรรหน่วยความจำของตัวสร้างการกำหนดและตัวสร้างการคัดลอก?

IDK หมายถึงอะไรโดยจัดสรรหน่วยความจำในกรณีนี้ แต่ถ้าคุณต้องการดูว่าเกิดอะไรขึ้นคุณสามารถ:

class A
{
public :
    A(){ cout<<"default constructor"<<endl;};
    A(const A& other){ cout<<"copy constructor"<<endl;};
    A& operator = (const A& other){cout <<"assignment operator"<<endl;}
};

ฉันขอแนะนำให้คุณดูที่:

เหตุใดจึงเรียกตัวสร้างการคัดลอกแทนตัวสร้างการแปลง

กฎสามข้อคืออะไร?


5

พูดง่ายๆว่า

ตัวสร้างการคัดลอกถูกเรียกเมื่ออ็อบเจ็กต์ใหม่ถูกสร้างขึ้นจากอ็อบเจ็กต์ที่มีอยู่เป็นสำเนาของอ็อบเจ็กต์ที่มีอยู่ และตัวดำเนินการกำหนดจะถูกเรียกเมื่ออ็อบเจ็กต์ที่เตรียมใช้งานแล้วถูกกำหนดค่าใหม่จากอ็อบเจ็กต์อื่นที่มีอยู่

ตัวอย่าง-

t2 = t1;  // calls assignment operator, same as "t2.operator=(t1);"
Test t3 = t1;  // calls copy constructor, same as "Test t3(t1);"

4

สิ่งที่ @Luchian Grigore Said ดำเนินการเช่นนี้

class A
{
public :
    int a;
    A(){ cout<<"default constructor"<<endl;};
    A(const A& other){ cout<<"copy constructor"<<endl;};
    A& operator = (const A& other){cout <<"assignment operator"<<endl;}
};

void main()
{
    A sampleObj; //Calls default constructor
    sampleObj.a = 10;

    A copyConsObj  = sampleObj; //Initializing calls copy constructor

    A assignOpObj; //Calls default constrcutor
    assignOpObj = sampleObj; //Object Created before so it calls assignment operator
}

เอาท์พุท


ตัวสร้างเริ่มต้น


คัดลอกตัวสร้าง


ตัวสร้างเริ่มต้น


ผู้ดำเนินการมอบหมาย



4

ความแตกต่างระหว่างตัวสร้างการคัดลอกและตัวสร้างการกำหนดคือ:

  1. ในกรณีของตัวสร้างการคัดลอกจะสร้างวัตถุใหม่ ( <classname> <o1>=<o2>)
  2. ในกรณีของตัวสร้างการกำหนดจะไม่สร้างอ็อบเจ็กต์ใด ๆ หมายความว่าใช้กับอ็อบเจ็กต์ที่สร้างไว้แล้ว ( <o1>=<o2>)

และฟังก์ชันพื้นฐานในทั้งสองเหมือนกันพวกเขาจะคัดลอกข้อมูลจาก o2 ไปยัง o1 สมาชิกโดยสมาชิก


2

ฉันต้องการเพิ่มอีกหนึ่งประเด็นในหัวข้อนี้ "ฟังก์ชันตัวดำเนินการของตัวดำเนินการมอบหมายควรเขียนเป็นฟังก์ชันสมาชิกของคลาสเท่านั้น" เราไม่สามารถทำให้มันเป็นฟังก์ชันเพื่อนซึ่งแตกต่างจากตัวดำเนินการไบนารีหรือยูนารีอื่น ๆ


1

สิ่งที่จะเพิ่มเกี่ยวกับตัวสร้างการคัดลอก:

  • เมื่อส่งผ่านวัตถุตามค่าจะใช้ตัวสร้างการคัดลอก

  • เมื่อวัตถุถูกส่งกลับจากฟังก์ชันตามค่าจะใช้ตัวสร้างการคัดลอก

  • เมื่อเริ่มต้นวัตถุโดยใช้ค่าของวัตถุอื่น (ตามตัวอย่างที่คุณให้)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.