티스토리 뷰

출처

C++의 멤버함수 중 클래스 안에서 직접 선언하지 않으면 컴파일러가 자동으로 선언해주는 함수들

: 기본형 생성자 / 복사생성자 / 복사대입연산자 /소멸자

 

Class Empty{};

//위와 아래는 서로 같다

Class Empty
{
public:
    Empty() {} // 생성자
    Empty(const Empty& ) {} // 복사생성자
    ~Empty() {} // 소멸자
    Empty& operator=(const Empty&) {} // 복사대입연산자
}

생성자

Empty() {}

출처

 

멤버 변수의 초기화

메모리에 생성된 객체는 모든 멤버 변수를 초기화하기 전에는 사용할 수 없다. 특히 객체의 멤버 중 private 멤버에는 직접 접근할 수 없기 때문에 일반적인 초기화 방식으로 초기화가 불가능하다.

따라서 private 멤버에 접근할 수 있는 초기화만을 위한 public 함수가 필요하고, 이런 초기화 함수는 객체가 생성된 후부터 사용되기 전까지 반드시 멤버의 초기화를 위해 호출되어야 한다.

 

생성자(Constructor)

  • C++에서 객체의 생성과 동시에 멤버 변수를 초기화해주는 멤버 함수. 객체 생성 시 딱 한 번 호출된다.
  • 생성자를 별도로 구현하지 않으면 기본적으로 기본생성자를 자동으로 생성한다.
  • 클래스 내부에 생성자를 직접 선언하면 컴파일러는 기본 생성자를 만들어주지 않는다. 
  • 매개변수 없이 인스턴스가 생성되는 것을 막을 때에는 기본생성자를 private로 선언하거나, public에서 선언 후 구현하지 않는다.

 

객체를 초기화하는 방법이 여러 개 존재할 경우에는 오버로딩 규칙에 따라 여러 개의 생성자를 가질 수 있다.

 

* 오버로딩: 중복정의(함수이름은 동일, 변수타입을 다르게) - 다양한 타입의 인수에 대해서 같은 기능을 하는 함수를 정의할 때 용이

* 오버라이딩: 재정의 - 자식이 부모를 무시할 때


멤버 이니셜라이저(member initializer)

객체 선언과 동시에 초기화가 이루어진다. 생성자 함수 뒤에 : 를 붙여서 사용한다.

class Zombie
{
private:
    string type;
    string name;

public:
    Zombie(string type, string name);
};

// 생성자 함수 : 생성 후 초기화
Zombie::Zombie(string type, string name)
{
    this->type = type;
    this->name = name;
}

// 멤버 이니셜라이저 : 생성과 동시에 초기화
Zombie::Zombie(string type, string name):
    type(type), name(name)
{}

 

멤버 이니셜라이저 사용 이유

출처

1. 선언과 동시에 초기화가 이뤄지기 때문에 생성되는 바이너리 코드 양이 달라 성능에 이점이 있다.

 

2. const 멤버 변수 초기화 (* const: 변수를 상수로 선언해 값을 변경할 수 없게 해주는 키워드)

const 는 선언과 동시에 초기화가 이루어져야 하므로 멤버 이니셜라이저를 사용해야 한다.

class Test{ 
  const int test; 
  Test(int x) : test(x){ }
};

 

3. 레퍼런스 멤버 변수 초기화

레퍼런스는 선언과 동시에 초기화가 이루어져야 한다. 레퍼런스 참고

class Test{ 
  int &test; 
  Test(int x) : test(x) { }
};

 

4. 멤버 객체 초기화

 

5. 상속 멤버 변수 초기화 상속 참고

class Parent
{
public:
  int x, y;
  Parent(int i, int j)
  { x = i; y = j; }
};

class Test : public Parent
{
public:
  int test;
  Test(int i, int j, int k) : Parent(i, j)
  { test = k; }       
};

얕은 복사 vs 깊은 복사

얕은 복사

- 단순 변수를 복사하는 경우에는 문제가 없다. 

- 멤버 변수가 힙의 메모리 공간을 참조하는 경우에 문제가 된다. (할당, 포인터 등) 참고

- (컴파일러가 자동적으로 생성해주는) 기본 복사생성자, 기본 복사대입연산자 사용 시 얕은 복사가 일어난다.

 

깊은 복사

- 기본복사생성자, 기본대입연산자를 오버로딩해서 깊은 복사를 구현한다.


복사생성자

Empty(const Empty& ) {}

복사생성자가 변수를 참조로 받는 이유: 복사 무한루프에 빠지지 않게 하기 위해서

출처

 


복사대입연산자

Empty& operator=(const Empty&) {}

 

연산자 오버로딩

(리턴 타입) operator(연산자) (연산자가 받는 인자)

 

대입연산자가 참조로 리턴하는 이유: 대입 연산이 사슬처럼 엮이는 상황에서의 관례

출처

 

자기대입

자기 자신에 대해 대입연산자를 사용하는 경우를 방지해야 한다.

  • 원본 객체와 복사대상 객체의 주소 비교
  • 문장의 순서 조절
  • 복사 후 맞바꾸기

 


대입연산자 vs 복사생성자

출처

  • If a new object has to be created before the copying can occur, the copy constructor is used (note: this includes passing or returning objects by value).
  • If a new object does not have to be created before the copying can occur, the assignment operator is used.

복사를 하면서 새 객체의 생성이 필요하면 복사생성자를 사용하고, 그렇지 않으면 대입연산자를 사용한다.

int main()
{
    // 복사생성자
    Dot dot1(1, 2);
    Dot dot2 = dot1;
    
    // 대입연산자
    Dot dot1(1, 2);
    Dot dot2(3, 4);
    dot2 = dot1;
}

 

출처

복사 생성자는 객체가 새로 생성되는 시점에서 대입을 할 때 호출이 되고 대입 연산자는 객체 두 개가 이미 생성 및 초기화가 진행된 상태에서 대입을 할 때 호출이 된다.


 

'42 > cpp' 카테고리의 다른 글

[C++] NULL 포인터로 객체의 함수가 호출되는 이유  (0) 2021.07.21
참조자  (0) 2021.05.26
메모리구조, 정적할당과 동적할당  (0) 2021.05.25
객체지향, 접근지정자  (0) 2021.05.25
네임스페이스, 표준입출력  (0) 2021.05.18
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함