[질문] C++ 복사생성자(copy constructor)

hermian의 이미지

#include <iostream>
using std::endl;
using std::cout;
 
class Point{
public:
    Point(int _x=0, int _y=0):x(_x), y(_y){
        cout << "Called Ctor" << endl;
    }
 
    Point(const Point &p) : x(p.x), y(p.y) {
        cout << "Called Copy ctor" << endl;
    }
    const Point& operator=(const Point& p)
    {
        cout << "Called operator= member function" <<endl;
 
        if (&p == this) return *this;
        x = p.x;
        y = p.y;
 
        return *this;
    }
    void ShowPosition() {
        cout << x << " " << y << endl;
    }
    Point operator+(const Point& p) const;
 
private:
    int x;
    int y;
};
Point Point::operator+(const Point& p) const
{
    cout << "1. operator+" << endl;
    Point temp(x+p.x, y+p.y);
    cout << "2. operator+" << endl;
 
    return temp; // called copy ctor
}
int main(void)
{
    Point p1(1, 2);
    Point p2(2, 1);
    Point p3=p1+p2;
    return 0;
}

/// result
Called Ctor
Called Ctor
1. operator+
Called Ctor
2. operator+

g++ --version
g++ (GCC) 3.3.1
Copyright (C) 2003 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Point p3=p1+p2;
의 경우 Ctor도 Copy Ctor도 operator=도 불리우지 않습니다.
operator+에서 Copy Ctor이 불리어야 하지 않는지요?

조언 부탁드립니다.

8월 25일 답변이 없어 오늘 봤더니 소스가 잘렸더군요.

hermian의 이미지

오늘 새로운 시험을 해봤습니다.
아래 소스의 main에서 p3의 생성자 호출이 없습니다.
대충 이해는 가지만, 정확히는 모르겠군요.
명확한 설명 좀 부탁드립니다.

#include <iostream>
using std::endl;
using std::cout;
 
class Point {
private:
    int x, y;
public:
    Point(int _x=0, int _y=0); // default value는 선언부분에...
    Point(const Point& p);
    ~Point();
 
    void ShowPosition();
    Point operator+(const Point& p) const;
    Point& operator=(const Point& p);
};
Point::Point(int _x, int _y)
  : x(_x), y(_y)
{
    cout << "Called Ctor : " << x << "," << y << " : " << this << endl;
}
Point::Point(const Point& p)
{
    cout << "Called Copy Ctor : " << this << endl;
    x = p.x;
    y = p.y;
}
Point::~Point()
{
    cout << "Called Xtor : " << this << endl;
}
 
void Point::ShowPosition(){
    cout<< this << " : " << x<<" "<<y<<endl;
}
Point Point::operator+(const Point& p) const{
    cout << "Called operator+ : " << this << endl;
    Point temp(x+p.x, y+p.y);
    cout << "end operator+" << endl;
    return temp;
}
Point& Point::operator=(const Point& p)
{
    cout << "Called operator= : " << this << endl;
    if (this == &p) return *this;
 
    x = p.x;
    y = p.y;
 
    return *this;
}
 
int main(void)
{
    Point p1(1, 2);
    Point p2(2, 1);
    Point p3=p1+p2; // !!! operator= 이냐 아니면 Copy Ctor이냐?
    p3.ShowPosition();
 
    return 0;
}

결과
Called Ctor : 1,2 : 0xfeffd140 <-- p1 생성자
Called Ctor : 2,1 : 0xfeffd130 <-- p2 생성자
Called operator+ : 0xfeffd140 <-- p1.operator+
Called Ctor : 3,3 : 0xfeffd120 <-- temp 생성자
end operator+
0xfeffd120 : 3 3 <-- temp.ShowPosition()
Called Xtor : 0xfeffd120 <-- temp 소멸자
Called Xtor : 0xfeffd130 <-- p2 소멸자
Called Xtor : 0xfeffd140 <-- p1 소멸자

thyoo의 이미지

Point p3=p1+p2;

위 문장은 definition이므로 '='는 initializer이지 assignment operator가 아닙니다.
ctor이 호출됩니다.

풀어 쓰면

Point p3(p1.operator+(p2));

원래는

Point Point::operator+(Point& rhs)
{
    Point temp.ctor(x+rhs.x, y+rhs.y);
    return temp;
}
 
Point p3.copy_ctor((안보이는 임시변수.copy_ctor(p1.operator+(p2))));

이겠으나

만약 컴파일러가 이렇게 한다면
컴파일러 지적 수준이 땡칠이므로 버리고 딴 거 쓰세요.

컴파일러가 쓸데 없는 copy ctor 호출을 피하는 겁니다.

id 만들 때 앞에 underscore '_'는 피하는 게 좋습니다.
(_XXX : C reserved, __XXX C++ reserved)
이름이 충돌하면 namespace 씌우면 되겠지만
MACRO에겐 소용 없지요.
그리고 destructor는 약자로 dtor입니다.
___________________________________
Less is More (Robert Browning)

___________________________________
Less is More (Robert Browning)

cinsk의 이미지

C++ 컴파일러에 따라 약간씩 달라질 수 있습니다.
원칙대로라면 temp를 만들고, 그 값이 copy constructor를 써서 temp에서 복사해야 하겠지만, 이 과정을 계속 하다보면 너무나도 느려질 가능성이 있습니다.

따라서 C++ 표준에는 임시 object A에서 copy constructor를 써서 object B를 만들 경우, 처음에 임시 object를 만들때 아예 B에서 만드는 것을 허락합니다. 따라서 p3의 경우, copy constructor가 쓰이지 않은 것입니다. 그리고 temp를 만드는 constructor 자체가 p3의 constructor에 해당합니다. (ISO C++ Standard 12.8.15)

--
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
Korean Ver: http://www.cinsk.org/cfaqs/

doldori의 이미지

부연하면 이를 return value optimization(RVO)라고 부릅니다.
gcc는 RVO에 의해 2개의 임시 개체의 생성을 회피했습니다.
operator+() 내의 temp와 이를 반환하면서 복사생성자에 의해 생기는 개체.
꽤 똑똑한 컴파일러죠.
이름이 붙은 임시 개체(temp 같은 것)에는 RVO를 적용하지 않는 컴파일러도 있습니다.
이런 경우

Point Point::operator+(const Point& p) const{
    Point temp(x+p.x, y+p.y);
    return temp;
}

이렇게 하기보다는
Point Point::operator+(const Point& p) const{
    return Point(x+p.x, y+p.y);
}

로 하면 RVO를 수행할 가능성이 더 높습니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.