스콧 마이어스의 Effective C++을 읽고 개인 공부 목적으로 요약 작성한 글입니다!
💡 컴파일러는 경우에 따라 클래스에 대해 기본 생성자, 복사 생성자, 복사 대입 연산자, 소멸자를 암시적으로 만들어 놓을 수 있다!
: 새로운 객체를 메모리에 만드는데 필요한 과정을 제어하고, 객체의 초기화를 맡는 함수
: 객체를 없앰 + 그 객체가 메모리에서 적절히 사라질 수 있도록 하는 과정을 제어하는 함수
: 기존의 객체에 다른 객체의 값을 줄 때 사용하는 함수
class Empty{};
만약 내가 위에 코드처럼 클래스를 선언했다면,
아래 코드에 있는 것들을 만든다고 봐도 된다.
컴파일러가 필요하다고 생각하면 걍 만들어준다.
class Empty {
public:
Empty() { ... } // 기본 생성자
Empty(const Empty& rhs) { ... } // 복사 생성자
~Empty() { ... } // 소멸자
Empty& operator= (const Empty& rhs) { ... } // 복사 대입 연산자
Empty e1; // 기본 생성자, 소멸자 필요
Empty e2(e1); // 복사 생성자 필요
e2 = e1; // 복사 대입 연산자 필요
컴파일러는 사용자가 생성자를 선언해놓지 않으면, 기본 생성자를 선언해놓는다.
(Public, inline)
생성자를 선언해놓았다면 컴파일러는 기본 생성자를 생성하지 않는다.
원본 객체의 비정적 데이터를 사본 객체 쪽으로 복사해준다.
복사 생성자가 하는 일과 거의 비슷하긴 함
최종 결과 코드가 '적법해야'하고, '이치에 닿아야만'한다.
안그러면 컴파일러는 operator=의 자동 생성을 해주지 않고, 직접 정의해줘야 함.
소멸자는 클래스가 상속한 기본 클래스의 소멸자가 가상 소멸자로 되어 있지 않으면
역시 비가상 소멸자로 만들어진다.
template<class T>
class NameObject {
public:
NameObject(string& name, const T& value);
...
private:
string& nameValue;
const T objectValue;
};
string newDog("Persephone");
string oldDog("Satch");
NameObject<int> p(newDog, 2);
NameObject<int> s(oldDog, 26);
p = s;
이런 경우,,
nameValue는 string에 대한 참조자 이고, objectValue는 상수이다.
p = s;
을 실행할 때, p에 들어있는 데이터 멤버는 어떻게 될까유?
C++의 참조자는 원래 자신이 참조하고 있는 것과 다른 객체를 참조할 수 없다.
그래서 참조자 자체는 바뀔 수 없다
p.nameValue가 참조하는 string 객체 자체가 바뀐다면, 실제 대입 연산에 직접적으로 관여하지 않는 객체도 영향을 받는다.
결국 컴파일 에러가 난다.
정말 기본 of 기본이지만,,!!
생성자 관련 부분은 넘 중요하고 꼭 알아야 하는 것 같다.