왜 제 생성자가 제대로 동작하지 않죠?
이 질문은 다음과 같은 많은 질문에서 뽑혔습니다 :
- 왜 컴파일러가 제가 원하지도 않을때 제 객체를 복사하나요?
- 어떻게 복사를 막을 수 있나요?
- 어떻게 암묵적 변환을 막을 수 있나요?
- 어떻게 제 int 값을 복소수 값으로 바꾸나요?
기본적으로 클래스는 모든 요소를 복사하는 복사 생성자와 복사 할당을 제공합니다.
예시:
struct Point {
int x,y;
Point(int xx = 0, int yy = 0) :x(xx), y(yy) { }
};
Point p1(1,2);
Point p2 = p1;
여기에 p2.x==p1.x and p2.y==p1.y 부분이 있습니다. 이런게 종종 정확히 여러분들이 원하던 것이겠지만 (그리고 C의 호환성을 위해 필수인 것), 다음도 고려해보세요:
class Handle {
private:
string name;
X* p;
public:
Handle(string n)
:name(n), p(0) { /* acquire X called "name" and let p point to it */ }
~Handle() { delete p; /* release X called "name" */ }
// ...
};
void f(const string& hh)
{
Handle h1(hh);
Handle h2 = h1; // leads to disaster!
// ...
}
여기서, 기본 복사의 동작은 h2.name==h1.name and h2.p==h1.p 입니다. 이 부분이 재앙으로 가는 길입니다. 우리가 f 함수에서 나가게 될 경우, h1과 h2의 소멸자가 불리게 되고, h1.p와 h2.p가 가리키던 객체가 두 번 delete 되게 됩니다.
어떻게하면 이러한 상황을 피할 수 있을까요? 가장 쉬운 방법은 복사하는 동작을 private으로 만들어 복사를 방지하는 것입니다.
class Handle {
private:
string name;
X* p;
Handle(const Handle&); // prevent copying
Handle& operator=(const Handle&);
public:
Handle(string n)
:name(n), p(0) { /* acquire the X called "name" and let p point to it */ }
~Handle() { delete p; /* release X called "name" */ }
// ...
};
void f(const string& hh)
{
Handle h1(hh);
Handle h2 = h1; // error (reported by compiler)
// ...
}
물론 복사가 필요하다면 복사 이니셜라이저와 복사 할당을 정의하여 원하는 의미로 제공할 수 있습니다.
Point로 돌아와서, 기본 복사의 동작이 괜찮지만 문제는 생성자입니다.
struct Point {
int x,y;
Point(int xx = 0, int yy = 0) :x(xx), y(yy) { }
};
void f(Point);
void g()
{
Point orig; // create orig with the default value (0,0)
Point p1(2); // create p1 with the default y-coordinate 0
f(2); // calls Point(2,0);
}
사람들은 orig와 p1에 사용되는 편리성을 위해 디폴트 매개변수(Default Arguments)를 제공합니다. 그런데, 몇몇은 사람들은 f()에서 호출되는 2에서 Point(2,0)으로의 변환에 대해 놀랄 것입니다. 이때, 1개의 매개변수를 받는 생성자가 변환으로 정의됩니다. 기본적으로 이것이 암묵적 변환입니다. 이러한 변환을 명시적으로 요구하려면 생성자를 explicit으로 선언하세요.
struct Point {
int x,y;
explicit Point(int xx = 0, int yy = 0) :x(xx), y(yy) { }
};
void f(Point);
void g()
{
Point orig; // create orig with the default value (0,0)
Point p1(2); // create p1 with the default y-coordinate 0
// that's an explicit call of the constructor
f(2); // error (attmpted implicit conversion)
Point p2 = 2; // error (attmpted implicit conversion)
Point p3 = Point(2); // ok (explicit conversion)
}
'번역 > Bjarne Stroustrup's C++ Style and Technique FAQ' 카테고리의 다른 글
Why do I have to put the data in my class declarations? (6) | 2019.01.31 |
---|---|
Why do my compiles take so long? (4) | 2019.01.15 |
Does "friend" violate encapsulation? (0) | 2018.12.19 |
Why isn't the destructor called at the end of scope? (2) | 2018.08.13 |
How do i define an in class constant? (0) | 2018.08.12 |
댓글