생성자에서 C ++ 개체 멤버 변수를 초기화하려면 어떻게해야합니까?
멤버 변수로 몇 개의 개체가있는 클래스가 있습니다. 선언 할 때 이러한 멤버에 대한 생성자가 호출되는 것을 원하지 않으므로 개체에 대한 포인터를 명시 적으로 유지하려고합니다. 내가 뭐하고 있는지 모르겠다. o_O
StackOverflow에서 개체 멤버 변수의 다른 예를 찾을 수있는 것 같지만 일반적으로 생성자는 다음과 같이 즉시 호출됩니다.
class MyClass {
public:
MyClass(int n);
private:
AnotherClass another(100); // this constructs AnotherClass right away!
};
하지만 MyClass
생성자가 생성자를 호출하기를 원합니다 AnotherClass
. 내 코드는 다음과 같습니다.
BigMommaClass.h
#include "ThingOne.h"
#include "ThingTwo.h"
class BigMommaClass {
public:
BigMommaClass(int numba1, int numba2);
private:
ThingOne* ThingOne;
ThingTwo* ThingTwo;
};
BigMommaClass.cpp
#include "BigMommaClass.h"
BigMommaClass::BigMommaClass(int numba1, int numba2) {
this->ThingOne = ThingOne(100);
this->ThingTwo = ThingTwo(numba1, numba2);
}
컴파일을 시도 할 때 발생하는 오류는 다음과 같습니다.
g++ -Wall -c -Iclasses -o objects/BigMommaClass.o classes/BigMommaClass.cpp
In file included from classes/BigMommaClass.cpp:1:0:
classes/BigMommaClass.h:12:8: error: declaration of âThingTwo* BigMommaClass::ThingTwoâ
classes/ThingTwo.h:1:11: error: changes meaning of âThingTwoâ from âclass ThingTwoâ
classes/BigMommaClass.cpp: In constructor âBigMommaClass::BigMommaClass(int, int)â:
classes/BigMommaClass.cpp:4:30: error: cannot convert âThingOneâ to âThingOne*â in assignment
classes/BigMommaClass.cpp:5:37: error: â((BigMommaClass*)this)->BigMommaClass::ThingTwoâ cannot be used as a function
make: *** [BigMommaClass.o] Error 1
올바른 접근 방식을 사용하고 있지만 잘못된 구문을 사용하고 있습니까? 아니면 다른 방향에서 가야할까요?
멤버 이니셜 라이저 목록에서 멤버를 초기화하는 방법을 지정할 수 있습니다.
BigMommaClass {
BigMommaClass(int, int);
private:
ThingOne thingOne;
ThingTwo thingTwo;
};
BigMommaClass::BigMommaClass(int numba1, int numba2)
: thingOne(numba1 + numba2), thingTwo(numba1, numba2) {}
작동하지 않을 (잘못된 구문) ThingOne
사용 하여을 (를) 만들려고합니다 operator=
. 또한 클래스 이름을 변수 이름으로 사용하고 ThingOne* ThingOne
있습니다. 먼저 변수 이름을 수정하겠습니다.
private:
ThingOne* t1;
ThingTwo* t2;
이것들은 포인터이기 때문에 무언가를 가리켜 야합니다. 객체가 아직 생성되지 않은 경우 BigMommaClass
생성자 에서 new를 사용하여 명시 적으로 생성해야합니다.
BigMommaClass::BigMommaClass(int n1, int n2)
{
t1 = new ThingOne(100);
t2 = new ThingTwo(n1, n2);
}
일반적으로 이니셜 라이저 목록은 생성에 선호되므로 다음과 같이 보입니다.
BigMommaClass::BigMommaClass(int n1, int n2)
: t1(new ThingOne(100)), t2(new ThingTwo(n1, n2))
{ }
이 질문은 약간 오래되었지만 C ++ 11에서 멤버 변수를 초기화하기 전에 생성자에서 "더 많은 작업을 수행"하는 또 다른 방법이 있습니다.
BigMommaClass::BigMommaClass(int numba1, int numba2)
: thingOne([](int n1, int n2){return n1+n2;}(numba1,numba2),
thingTwo(numba1, numba2) {}
위의 람다 함수가 호출되고 결과가 thingOnes 생성자에 전달됩니다. 물론 람다를 원하는만큼 복잡하게 만들 수 있습니다.
나는 이것이 5 년 후라는 것을 알고 있지만, 위의 답변은 귀하의 소프트웨어에 무엇이 잘못되었는지를 다루지 않습니다. (글쎄 Yuushi는 그렇습니다. 그러나 나는 이것을 타이핑 할 때까지 깨닫지 못했습니다 – doh!). 그들은 생성자에서 C ++ 객체 멤버 변수를 어떻게 초기화 할 수 있습니까? 라는 제목의 질문에 답합니다 . 이것은 다른 질문에 관한 것입니다. 올바른 접근 방식을 사용하고 있지만 잘못된 구문을 사용하고 있습니까? 아니면 다른 방향에서 가야할까요?
프로그래밍 스타일은 대부분 의견의 문제이지만 생성자에서 가능한 한 많은 작업을 수행하는 것에 대한 대안은 생성자를 최소한으로 유지하는 것이며, 종종 별도의 초기화 함수를 사용하는 것입니다. 모든 초기화를 생성자에 넣을 필요가 없습니다. 때때로 생성자 초기화 목록에 강제로 넣는 것을 신경 쓰지 마십시오.
그렇다면, 소프트웨어에 어떤 문제가 있었습니까?
private:
ThingOne* ThingOne;
ThingTwo* ThingTwo;
이 줄 뒤에 ThingOne
(및 ThingTwo
)은 컨텍스트에 따라 두 가지 의미를 갖습니다.
BigMommaClass 외부에서 ThingOne
생성 한 클래스는#include "ThingOne.h"
BigMommaClass 내부 ThingOne
에는 포인터가 있습니다.
그것은 컴파일러가 라인을 이해할 수 있고 그 자체가 포인터 인 무언가에 대한 포인터 라고 생각하는 루프에 갇히지 않는다고 가정합니다 ThingOne
...
나중에 쓸 때
this->ThingOne = ThingOne(100);
this->ThingTwo = ThingTwo(numba1, numba2);
BigMommaClass
당신의 내부 ThingOne
는 포인터 라는 것을 명심 하십시오 .
접두사 (p)를 포함하도록 포인터 선언을 변경하는 경우
private:
ThingOne* pThingOne;
ThingTwo* pThingTwo;
그런 다음 ThingOne
항상 클래스와 pThingOne
포인터를 참조합니다 .
그런 다음 다시 작성할 수 있습니다.
this->ThingOne = ThingOne(100);
this->ThingTwo = ThingTwo(numba1, numba2);
같이
pThingOne = new ThingOne(100);
pThingTwo = new ThingTwo(numba1, numba2);
이중 의미 문제와 누락 된 new
. (원한다면 떠날 수 있습니다 this->
!) 그 자리에 다음 줄을 내 C ++ 프로그램에 추가 할 수 있으며 잘 컴파일됩니다.
class ThingOne{public:ThingOne(int n){};};
class ThingTwo{public:ThingTwo(int x, int y){};};
class BigMommaClass {
public:
BigMommaClass(int numba1, int numba2);
private:
ThingOne* pThingOne;
ThingTwo* pThingTwo;
};
BigMommaClass::BigMommaClass(int numba1, int numba2)
{
pThingOne = new ThingOne(numba1 + numba2);
pThingTwo = new ThingTwo(numba1, numba2);
};
당신이 쓸 때
this->ThingOne = ThingOne(100);
this->ThingTwo = ThingTwo(numba1, numba2);
의 사용은 this->
컴파일러에게 왼쪽 ThingOne
이 포인터를 의미한다는 것을 알려줍니다 . 그러나 우리는 BigMommaClass
당시 내부 에 있으며 필요하지 않습니다. 문제는 ThingOne
클래스를 의미하는 등호의 오른쪽에 있습니다. 따라서 문제를 해결하는 또 다른 방법은
this->ThingOne = new ::ThingOne(100);
this->ThingTwo = new ::ThingTwo(numba1, numba2);
또는 단순히
ThingOne = new ::ThingOne(100);
ThingTwo = new ::ThingTwo(numba1, numba2);
::
컴파일러의 식별자 해석을 변경하는 데 사용 합니다.
나는 (또한 다른 사람들이 언급했듯이)이 질문이 오래되었다는 사실을 알고 있지만 , 반원들이 처한 상황에 대한 해결책을 제안한 @chris 의 첫 번째 ( 그리고 훌륭한 ) 답변 에 관한 것을 지적하고 싶었 습니다. 는 "같은 진정한 복합 "회원 (ie- NOT 과 같은 포인터 NOR 참조 ). 메모가 약간 크므로 여기에 샘플 코드를 사용하여 설명하겠습니다.
앞서 언급 한대로 회원을 보유하기로 선택했을 때 다음 두 가지 사항도 염두에 두어야합니다.
1) For every "composed object" that DOES NOT have a default ctor - you MUST initialize it in the initialization list of ALL the ctor's of the "father" class (i.e.- BigMommaClass
or MyClass
in the original examples and MyClass
in the code below), in case there are several (see InnerClass1
in the example below). Meaning, you can "comment out" the m_innerClass1(a)
and m_innerClass1(15)
ONLY if you enable the InnerClass1
default ctor.
2) For every "composed object" that DOES have a default ctor - you MAY initialize it within the initialization list, but it will work also if you chose not to (see InnerClass2
in the example below).
See sample code (complied under Ubuntu 18.04 with g++
version 7.3.0):
#include <iostream>
using namespace std;
class InnerClass1
{
public:
InnerClass1(int a) : m_a(a)
{
cout << "InnerClass1::InnerClass1 - set m_a:" << m_a << endl;
}
/* No default cotr
InnerClass1() : m_a(15)
{
cout << "InnerClass1::InnerClass1() - set m_a:" << m_a << endl;
}
*/
~InnerClass1()
{
cout << "InnerClass1::~InnerClass1" << endl;
}
private:
int m_a;
};
class InnerClass2
{
public:
InnerClass2(int a) : m_a(a)
{
cout << "InnerClass2::InnerClass2 - set m_a:" << m_a << endl;
}
InnerClass2() : m_a(15)
{
cout << "InnerClass2::InnerClass2() - set m_a:" << m_a << endl;
}
~InnerClass2()
{
cout << "InnerClass2::~InnerClass2" << endl;
}
private:
int m_a;
};
class MyClass
{
public:
MyClass(int a, int b) : m_innerClass1(a), /* m_innerClass2(a),*/ m_b(b)
{
cout << "MyClass::MyClass(int b) - set m_b to:" << m_b << endl;
}
MyClass() : m_innerClass1(15), /*m_innerClass2(15),*/ m_b(17)
{
cout << "MyClass::MyClass() - m_b:" << m_b << endl;
}
~MyClass()
{
cout << "MyClass::~MyClass" << endl;
}
private:
InnerClass1 m_innerClass1;
InnerClass2 m_innerClass2;
int m_b;
};
int main(int argc, char** argv)
{
cout << "main - start" << endl;
MyClass obj;
cout << "main - end" << endl;
return 0;
}
'Development Tip' 카테고리의 다른 글
활성 메뉴 항목-asp.net mvc3 마스터 페이지 (0) | 2020.11.17 |
---|---|
Google C ++ 스타일 가이드의 예외 없음 규칙; (0) | 2020.11.17 |
matplotlib에 선 스타일 목록이 있습니까? (0) | 2020.11.17 |
삭제하지 않고 Github 포크 해제 (0) | 2020.11.17 |
열거 형 값 검증 (0) | 2020.11.17 |