6.4.5. 복사 생성자와 복사 대입연산자.
얕은 복사를 야기하는 암시적 복사 생성자와 복사 대입 연산자.
소스코드
-> 암시적 복사 생성자로 인해 pA가 가리키는 곳과 B가 가리키는 곳의 메모리가 동일해진다.
delete pA로 지우면 동일한 메모리를 가리키는 B의 참조값이 null값이 되어
문제가 발생하는데
이를 얕은 복사라고 한다.
이러한 문제를 해결하기 위해 개발자는 명시적으로 복사 생성자를 만들어줘야 한다.
6.4.6 객체의 값에 의한 호출
: CObject a(b), CObject a = b; 를 통해 복사 생성자의 호출은
이런식으로 2번의 경우에 발생한다는 것을 확인했다.
하지만 또 다른 하나가 있다.
바로바로!! 객체의 값에 의한 호출이다.
void func(CObject a)
{
}
-> 디폴트 생성자도 아닌 복사 생성자가 호출된다.
그로 인해 함수가 종료될때 지역값이므로 소멸자도 호출된다.
비용이 크므로 객체를 매개변수로 보낼때는 레퍼런스를 사용하도록 권장한다.
아니 반드시 레퍼런스타입으로 매개변수를 사용해야 한다.
-
매개변수로 객체를 value값으로 사용할때
-
레퍼런스타입으로 보내자.
-> 불필요한 객체 생성을 차단했다.
=> 추가적으로 매개변수로 들어오는 값 변경을 막기위해 const도 붙이자!
알게 된점.
- 레퍼런스 타입과 value 타입을 오버로딩 형식으로 사용할 수 있을까?
-> 모호성이 발생한다.
복사 생성자는 언제 호출되는 것일까?
- 객체 선언과 동시에 다른 객체를 괄호로 감싸 초기화할 때
- 객체 선언과 동시에 다른 객체를 대입연산할때
- 함수의 매개변수로 객체를 value값으로 사용할때
- 함수내의 임시 객체를 반환할 때.
6.4.7 복사를 방지하는 방법
-
- 클래스의 public 에 복사생성자와 대입연산자를 선언하는 방법이 있다.
-> 이때는 링크에러가 발생한다.
-
- 클래스의 private에다가 복사생성자와 대입연산자를 선언하자.
-> 이때는 컴파일에러가 발생한다.
//링크에러보다는 컴파일에러가 좋다.
-
- noncopyable 클래스를 사용하자.
: 상속 한후, 복사를 진행하려고 하면, 부모 클래스에 대한 복사도 이루어져야 한다. 부모의 복사 생성을 통제함으로써 복사가 이루어지려고 하는 코드를
막을수 있다.
이 그림처럼. protected를 사용한 이유는 noncopyable 클래스를 독립적으로
사용하지 않기 위함이다.
6.4.8 RValue Reference의 이용
명시적으로 만든 복사 생성자로 인해 효율이 떨어지는 경우가 있다!
- 클래스의 public 에 복사생성자와 대입연산자를 선언하는 방법이 있다.
-> 이때는 링크에러가 발생한다.
- 클래스의 private에다가 복사생성자와 대입연산자를 선언하자.
-> 이때는 컴파일에러가 발생한다.
//링크에러보다는 컴파일에러가 좋다.
- noncopyable 클래스를 사용하자.
: 상속 한후, 복사를 진행하려고 하면, 부모 클래스에 대한 복사도 이루어져야 한다. 부모의 복사 생성을 통제함으로써 복사가 이루어지려고 하는 코드를
막을수 있다.
이 그림처럼. protected를 사용한 이유는 noncopyable 클래스를 독립적으로
사용하지 않기 위함이다.
명시적으로 만든 복사 생성자로 인해 효율이 떨어지는 경우가 있다!
: 바로 함수내의 객체를 반환하는 경우, 복사 생성자가 호출된다.
생각해보면 이미 사라질 객체를 반환하면 복사 생성자로 인해 다른 객체의 메모리가 생성이 되는데, 사라질 객체의 메모리를 delete 하는 부분을 차단하고,
그 객체를 받아와 쓴다면 오히려 메모리를 아낄 수 있다.
뭔소리고?? 어떻게 하냐면? rValue 참조를 사용하면 된다.
-
복사 생성자로 메모리가 두번 생성하는 코드
-
Rvalue reference를 통해 메모리 재활용하는 코드
: rValue 참조를 이용해 메모리 할당이 한번만 발생했다.
소스코드
#include <iostream>
using namespace std;
class CText
{
public :
CText(const char *Text)
{
cout << "constructor " << endl;
mLen = strlen(Text);
mText = new char[mLen + 1];
strcpy(mText, Text);
}
CText(const CText& obj)
{
cout << "copy constructor " << endl;
mText = new char[obj.mLen + 1];
cout << "memory allocation " << endl;
strcpy(mText, obj.mText);
mLen = obj.mLen;
}
CText(CText&& obj)
{
cout << "move constructor " << endl;
mText = obj.mText;
mLen = obj.mLen;
obj.mText = nullptr;
obj.mLen = 0;
}
~CText() {
cout << " destructor " << endl;
if (mText)
{
cout << "memory delete " << endl;
delete[] mText;
mLen = 0;
}
}
char *mText;
int mLen;
};
CText GetText()
{
CText temp("hi world");
return temp;
}
int main()
{
CText t1 = GetText();
}
알게된점.
임시객체를 반환한다면, 복사 생성으로 메모리 하나 생성이 되는 것을
방지하기 위해 rValue 참조를 사용하자!
6.4.9 등가 연산자.
: 기본타입형에서 같다 다르다는 메모리의 크기나 비트 상태를 비교하는 것이 아니다. 값을 비교하는 것이다.
: double과 int의 메모리 구조가 다르지만, 값이 같으므로 같다라는 출력이 나오는 것을 확인할 수 있다.
클래스에서는 다르다.
: 왜냐하면 대입연산자를 여러개 정의할 수 있기 때문이다.
- 클래스에서는 등가 연산이 오버로딩되는 것을 확인할 수 있따.
=> 따라서 해당 타입에 맞게 오버로딩 함수를 제작해야 한다.
Author And Source
이 문제에 관하여(6.4.5. 복사 생성자와 복사 대입연산자.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@kwt0124/6.4.5.-복사-생성자와-복사-대입연산자저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)