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


91

ภายในและเกี่ยวกับโค้ดที่สร้างขึ้นมีความแตกต่างระหว่าง:

MyClass::MyClass(): _capacity(15), _data(NULL), _len(0)
{
}

และ

MyClass::MyClass()
{
  _capacity=15;
  _data=NULL;
  _len=0
}

ขอบคุณ ...

คำตอบ:


63

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


18
ดังที่ Richard กล่าวไว้มันสร้างความแตกต่างหากค่าเหล่านั้นเป็นประเภทดั้งเดิมและ const รายการเริ่มต้นเป็นวิธีเดียวในการกำหนดค่าให้กับสมาชิก const
thbusch

12
ใช้งานได้เฉพาะตามที่อธิบายไว้เมื่อตัวแปรไม่ใช่การอ้างอิงหรือค่าคงที่หากเป็นค่าคงที่หรือการอ้างอิงมันจะไม่รวบรวมโดยไม่ใช้รายการเริ่มต้น
stefanB

77

คุณต้องใช้รายการเริ่มต้นเพื่อเริ่มต้นสมาชิกค่าคงที่การอ้างอิงและคลาสพื้นฐาน

เมื่อคุณต้องการเริ่มต้นสมาชิกค่าคงที่การอ้างอิงและส่งผ่านพารามิเตอร์ไปยังตัวสร้างคลาสพื้นฐานตามที่กล่าวไว้ในความคิดเห็นคุณต้องใช้รายการเริ่มต้น

struct aa
{
    int i;
    const int ci;       // constant member

    aa() : i(0) {} // will fail, constant member not initialized
};

struct aa
{
    int i;
    const int ci;

    aa() : i(0) { ci = 3;} // will fail, ci is constant
};

struct aa
{
    int i;
    const int ci;

    aa() : i(0), ci(3) {} // works
};

ตัวอย่างคลาส / โครงสร้าง (ไม่ครบถ้วนสมบูรณ์) มีการอ้างอิง:

struct bb {};

struct aa
{
    bb& rb;
    aa(bb& b ) : rb(b) {}
};

// usage:

bb b;
aa a(b);

และตัวอย่างการเตรียมใช้งานคลาสฐานที่ต้องการพารามิเตอร์ (เช่นไม่มีตัวสร้างเริ่มต้น):

struct bb {};

struct dd
{
    char c;
    dd(char x) : c(x) {}
};

struct aa : dd
{
    bb& rb;
    aa(bb& b ) : dd('a'), rb(b) {}
};

4
และถ้า_capacity, _dataและ_lenมีชนิดชั้นโดยไม่ต้องมีการก่อสร้างเริ่มต้นที่สามารถเข้าถึงได้?
CB Bailey

2
คุณเรียกสิ่งที่ตัวสร้างที่มีอยู่หากคุณต้องการตั้งค่าเพิ่มเติมคุณจะเรียกสิ่งเหล่านั้นจากตัวสร้างของคุณ ความแตกต่างที่นี่คือคุณไม่สามารถเริ่มต้นconstสมาชิกในเนื้อหาของตัวสร้างได้คุณต้องใช้รายการการเริ่มต้น - constสมาชิกที่ไม่ใช่สามารถเริ่มต้นได้ในรายการเริ่มต้นหรือในเนื้อหาของตัวสร้าง
stefanB

@stefan: คุณไม่สามารถเรียกผู้สร้างได้ คลาสที่ไม่มีตัวสร้างเริ่มต้นจะต้องถูกเตรียมใช้งานในรายการ initializer เช่นเดียวกับสมาชิก const
GManNickG

2
คุณต้องเริ่มต้นการอ้างอิงในรายการเริ่มต้น
Andriy Tylychko

2
@stefanB: ฉันขอโทษถ้าฉันบอกเป็นนัยว่าคุณไม่เข้าใจสิ่งนี้เมื่อคุณทำจริง ในคำตอบของคุณคุณได้ระบุไว้เฉพาะเมื่อต้องตั้งชื่อฐานหรือสมาชิกในรายการเริ่มต้นคุณยังไม่ได้อธิบายความแตกต่างของแนวคิดระหว่างการเริ่มต้นในรายการเริ่มต้นและการกำหนดในเนื้อหาตัวสร้างซึ่งเป็นความเข้าใจผิดของฉัน อาจมาจาก
CB Bailey

18

ใช่. ในกรณีแรกที่คุณสามารถประกาศ_capacity, _dataและ_lenเป็นค่าคงที่:

class MyClass
{
private:
    const int _capacity;
    const void *_data;
    const int _len;
// ...
};

สิ่งนี้มีความสำคัญหากคุณต้องการตรวจสอบความเป็นไปได้constของตัวแปรอินสแตนซ์เหล่านี้ในขณะที่คำนวณค่าที่รันไทม์ตัวอย่างเช่น:

MyClass::MyClass() :
    _capacity(someMethod()),
    _data(someOtherMethod()),
    _len(yetAnotherMethod())
{
}

constอินสแตนซ์ต้องถูกเตรียมใช้งานในรายการ initializer หรือประเภทพื้นฐานต้องจัดเตรียมคอนสตรัคเตอร์แบบไม่มีพารามิเตอร์สาธารณะ (ซึ่งประเภทดั้งเดิมทำ)


2
เช่นเดียวกับการอ้างอิง หากชั้นเรียนของคุณมีสมาชิกอ้างอิงพวกเขาจะต้องเริ่มต้นในรายการเริ่มต้น
Mark Ransom

7

ฉันคิดว่าลิงค์นี้http://www.cplusplus.com/forum/articles/17820/ให้คำอธิบายที่ยอดเยี่ยม - โดยเฉพาะสำหรับผู้ที่เพิ่งเริ่มใช้ C ++

สาเหตุที่รายการ intialiser มีประสิทธิภาพมากกว่าคือภายในตัวสร้างจะมีการกำหนดเฉพาะการกำหนดเท่านั้นไม่ใช่การเริ่มต้น ดังนั้นหากคุณกำลังจัดการกับประเภทที่ไม่ใช่ในตัวตัวสร้างเริ่มต้นสำหรับวัตถุนั้นจึงถูกเรียกก่อนที่จะป้อนเนื้อหาของตัวสร้าง ภายในตัวสร้างคุณกำลังกำหนดค่าให้กับวัตถุนั้น

ผลนี้เป็นการเรียกไปยังตัวสร้างเริ่มต้นตามด้วยการเรียกไปยังตัวดำเนินการกำหนดสำเนา รายการเริ่มต้นช่วยให้คุณสามารถเรียกตัวสร้างการคัดลอกได้โดยตรงและบางครั้งอาจเร็วกว่ามาก (โปรดจำไว้ว่ารายการตัวเริ่มต้นอยู่ก่อนเนื้อหาของตัวสร้าง)


5

ฉันจะเพิ่มว่าถ้าคุณมีสมาชิกประเภทคลาสที่ไม่มีตัวสร้างเริ่มต้นการเริ่มต้นเป็นวิธีเดียวในการสร้างคลาสของคุณ


3

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


2

ขึ้นอยู่กับประเภทที่เกี่ยวข้อง ความแตกต่างมีความคล้ายคลึงกันระหว่าง

std::string a;
a = "hai";

และ

std::string a("hai");

โดยที่รูปแบบที่สองคือรายการเริ่มต้นนั่นคือจะสร้างความแตกต่างหากประเภทต้องการอาร์กิวเมนต์ตัวสร้างหรือมีประสิทธิภาพมากกว่าด้วยอาร์กิวเมนต์ตัวสร้าง


1

ความแตกต่างที่แท้จริงคือวิธีที่คอมไพเลอร์ gcc สร้างรหัสเครื่องและวางหน่วยความจำ อธิบาย:

  • (เฟส 1) ก่อนร่าง init (รวมรายการ init): คอมไพลเลอร์จัดสรรหน่วยความจำที่ต้องการสำหรับคลาส ชั้นยังมีชีวิตอยู่แล้ว!
  • (เฟส 2) ในตัวเริ่มต้น: เนื่องจากหน่วยความจำได้รับการจัดสรรตอนนี้ทุกการมอบหมายจะบ่งชี้การดำเนินการกับตัวแปรที่ออก / 'เริ่มต้นแล้ว'

มีวิธีอื่น ๆ ในการจัดการกับสมาชิกประเภท const อย่างแน่นอน แต่เพื่อให้ชีวิตของพวกเขาง่ายขึ้นนักเขียนคอมไพเลอร์ gcc จึงตัดสินใจตั้งกฎเกณฑ์บางอย่าง

  1. สมาชิกประเภท const ต้องถูกเตรียมใช้งานก่อนร่างกายเริ่มต้น
  2. หลังจากเฟส 1 การดำเนินการเขียนใด ๆ ใช้ได้เฉพาะกับสมาชิกที่ไม่คงที่

1

มีเพียงวิธีเดียวในการเตรียมใช้งานอินสแตนซ์คลาสพื้นฐานและตัวแปรสมาชิกที่ไม่คงที่และนั่นคือการใช้รายการตัวเริ่มต้น

หากคุณไม่ได้ระบุตัวแปรสมาชิกฐานหรือไม่คงที่ในรายการตัวเริ่มต้นของตัวสร้างของคุณสมาชิกหรือฐานนั้นจะถูกกำหนดค่าเริ่มต้นโดยปริยาย (ถ้าสมาชิก / ฐานเป็นประเภทคลาสที่ไม่ใช่ POD หรืออาร์เรย์ของคลาสที่ไม่ใช่ POD ประเภท) หรือปล่อยไว้โดยไม่ได้เริ่มต้นเป็นอย่างอื่น

เมื่อเข้าสู่เนื้อหาตัวสร้างแล้วฐานหรือสมาชิกทั้งหมดจะถูกกำหนดค่าเริ่มต้นหรือปล่อยทิ้งไว้โดยไม่ได้กำหนดค่าเริ่มต้น (กล่าวคือจะมีค่าที่ไม่แน่นอน) ไม่มีโอกาสในตัวสร้างที่จะมีอิทธิพลต่อวิธีการเริ่มต้นของพวกเขา

คุณอาจกำหนดค่าใหม่ให้กับสมาชิกในเนื้อหาตัวสร้างได้ แต่ไม่สามารถกำหนดให้กับconstสมาชิกหรือสมาชิกของประเภทชั้นเรียนที่ไม่สามารถกำหนดได้และไม่สามารถเชื่อมโยงการอ้างอิงซ้ำได้

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

หากคุณล้มเหลวในการตั้งชื่อสมาชิกหรือฐานในรายการตัวเริ่มต้นและเอนทิตีนั้นเป็นข้อมูลอ้างอิงมีประเภทคลาสที่ไม่มีตัวสร้างเริ่มต้นที่ผู้ใช้ประกาศซึ่งสามารถเข้าถึงได้constมีคุณสมบัติและมีประเภท POD หรือเป็นประเภทคลาส POD หรืออาร์เรย์ของประเภทคลาส POD มีconstสมาชิกที่มีคุณสมบัติเหมาะสม (โดยตรงหรือโดยอ้อม) แสดงว่าโปรแกรมมีรูปแบบไม่ถูกต้อง


0

หากคุณเขียนรายการเริ่มต้นคุณจะทำทั้งหมดในขั้นตอนเดียว หากคุณไม่เขียนรายการตัวเริ่มต้นคุณจะทำ 2 ขั้นตอนคือขั้นตอนหนึ่งสำหรับการประกาศและอีกขั้นสำหรับการกำหนดค่า


0

มีความแตกต่างระหว่างรายการเริ่มต้นและคำสั่งเริ่มต้นในตัวสร้าง ลองพิจารณาโค้ดด้านล่าง:

#include <initializer_list>
#include <iostream>
#include <algorithm>
#include <numeric>

class MyBase {
public:
    MyBase() {
        std::cout << __FUNCTION__ << std::endl;
    }
};

class MyClass : public MyBase {
public:
    MyClass::MyClass() : _capacity( 15 ), _data( NULL ), _len( 0 ) {
        std::cout << __FUNCTION__ << std::endl;
    }
private:
    int _capacity;
    int* _data;
    int _len;
};

class MyClass2 : public MyBase {
public:
    MyClass2::MyClass2() {
        std::cout << __FUNCTION__ << std::endl;
        _capacity = 15;
        _data = NULL;
        _len = 0;
    }
private:
    int _capacity;
    int* _data;
    int _len;
};

int main() {
    MyClass c;
    MyClass2 d;

    return 0;
}

เมื่อใช้ MyClass สมาชิกทั้งหมดจะถูกเตรียมใช้งานก่อนคำสั่งแรกในตัวสร้างที่ดำเนินการ

แต่เมื่อใช้ MyClass2 สมาชิกทั้งหมดจะไม่ถูกเตรียมใช้งานเมื่อคำสั่งแรกในตัวสร้างดำเนินการ

ในภายหลังอาจมีปัญหาการถดถอยเมื่อมีคนเพิ่มรหัสในตัวสร้างก่อนที่สมาชิกบางคนจะเริ่มต้น


0

นี่คือประเด็นที่ฉันไม่เห็นคนอื่นอ้างถึง:

class temp{
public:
   temp(int var);
};

คลาส temp ไม่มี ctor เริ่มต้น เมื่อนำไปใช้ในคลาสอื่นดังนี้

class mainClass{
public:
 mainClass(){}
private:
  int a;
  temp obj;
};

โค้ดจะไม่คอมไพล์ทำให้คอมไพลเลอร์ไม่ทราบวิธีการเริ่มต้นobjเนื่องจากมีเพียง ctor ที่ชัดเจนซึ่งได้รับค่า int ดังนั้นเราจึงต้องเปลี่ยน ctor ดังนี้:

mainClass(int sth):obj(sth){}

ดังนั้นจึงไม่ใช่แค่เรื่อง const และการอ้างอิงเท่านั้น!

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