*복사생성자 (기본으로 생성되는 것 중 하나)
의미 : 클래스를 복사하여 생성하는 생성자
기본생성자 - 단 한개의 생성자라도 명시적으로 구현되어 있으면 생성 안 됨
복사생성자 - 단 한개의 생성자라도 명시적으로 구현되어 있으면 상성 안 됨
(기본생성자와 마찬가지)
<속도 비교>
복사생성자 > 기본생성자
복사생성자 : 기본생성자를 생성 + 오버로딩까지 해야 하니 더 느림
<그럼에도 복사생성자 쓰는 이유>
기존: 하나하나 생성자로 만들면 몬스터 10마리 만들 때 멤버변수가 20개면 총 200번의 초기화를 해야한다.
현재: 복사생성자를 사용하면 메모리 전체를 복사하기 때문에 1:1 초기화를 할 필요 없이 한번에 메모리를 전부 복사해준다. (다수의 멤버 복사 시 이득을 보게 된다.)
<복사생성자 발생 시점 3가지>
1. 먼저 만든 객체를 매개 변수로 전달하여 객체를 생성하는 경우
#include "stdafx.h"
class CObj
{
public:
CObj(int _iA)
{
m_iA = _iA;
cout << "매개변수가 있는 생성자 호출" << endl;
}
private:
int m_iA;
int m_iB = 10; int m_iC = 20;
};
int main()
{
CObj Origin(100);
Origin.Render();
CObj Copy(Origin); //1.먼저 만든 객체를 매개변수로 전달하여 객체를 생성하는 경우
Copy.Render();
return 0;
}
출력 결과:
매개변수가 있는 생성자 호출
100 10 20
100 10 20
=> 복사생성자를 명시적으로 만들지 않아도 default 복사생성자가 만들어져
알아서 기본생성자에서 부여한 매개변수의 값 그대로 잘 출력한다.
<만약, 복사생성자를 명시적으로 생성한다면?>
CObj(const CObj& rhs) //복사생성자의 명시적 호출은 이런식으로 씁니다.
{
m_iA = rhs.m_iA; //원본 객체를 사본 객체에 복사한다는 의미
//명시적 호출해줄 때에는 이렇게 일일히 할당해줘야 제대로된 결과가 나옴
cout << "복사 생성자 호출" << endl;
}
<복사생성자 매개변수의 의미>
& : 원본참조
const: 읽기 전용
rhs : right hand side(오른손 ) - 복사생성자 매개변수(원본객체)로 많이 씀
총합 : "원본을 읽기 전용으로 참조하겠다."
출력 결과:
매개변수가 있는 생성자 호출
100 10 20
복사 생성자 호출 //==> 복사생성자는 default기본생성자가 아닌 default복사생성자를 부른다.
100 10 20
2. 함수의 매개 변수가 객체 타입이어서 복사 생성을 수행
#include "stdafx.h"
class CObj
{
public:
CObj(int _iA)
{
m_iA = _iA;
cout << "매개변수가 있는 생성자 호출" << endl;
}
public:
void Output(CObj Temp) { //2.함수의 매개변수가 객체 타입이어서 복사 생성을 수행.
Temp.Render();
}
private:
int m_iA;
};
int main()
{
CObj Test(200);
Origin.Output(Test);//깊은복사가 이루어짐.(이 때만 200으로 나오고 Origin은 100..)
return 0;
}
깊은복사가 이루어져 call by value로 값이 복사되어 전달된다.
3. 함수의 반환 타입이 객체 타입이어서 복사 생성을 수행
#include "stdafx.h"
class CObj
{
public:
CObj(int _iA)
{
m_iA = _iA;
cout << "매개변수가 있는 생성자 호출" << endl;
}
CObj(const CObj& rhs)
{
m_iA = rhs.m_iA;
cout << "복사 생성자 호출" << endl;
}
public:
void Render() { cout << m_iA << " " << m_iB << " " << m_iC << endl; }
CObj Get_Obj()
{
return *this;
}
private:
int m_iA;
int m_iB = 10; int m_iC = 20;
};
int main()
{
CObj Origin(100);
Origin.Get_Obj().Render();
return 0;
}
출력결과:
복사 생성자 호출
100 10 20
이 세가지 경우 복사생성자를 호출한다.
2,3은 볼 일이 희박(대부분 우리가 만드는 함수들은 포인터를 활용하지 객체를 활용하지 않음)
복사생성자는 구조체에서도 쓰임..
(ex) 텍알에서 복사생성자 쓰인 예시 Get_Info() => 3)
(ex2) INFO Temp(m_tInfo); => 2)
(ex3) 프로토타입 디자인패턴
CObj* Create() { return new CObj(*this); } => 3)
<240206.cpp>
#include "stdafx.h"
#include <stdio.h>
#include <iostream>
#include "Player.h"
using namespace std;
int g_iNumber = 10;
class CName
{
public:
CName(const char* pName)
{
int iLength = strlen(pName) + 1;
m_pName = new char[iLength];
strcpy_s(m_pName, iLength, pName);
}
~CName() { delete[] m_pName; }
public:
void PrintN() { printf("%s\n", m_pName); }
private:
char* m_pName;
};
void Draw(CName Name) { Name.PrintN(); }
int main()
{
CName Name("neo - peple");
Draw(Name);
CPlayer player;
player.Insert();
return 0;
}
컴파일 결과:
neo-peple이 출력된 후 컴파일 에러
(디폴트복사생성자로 복사 자체는 문제 없음
프로그램이 돌고 나서 터짐 => 런타임 오류
소멸자 호출 시점에 문제 발생)
<문제 원인>
소멸자 호출 시점에 문제 발생
둘다 힙공간에 있는것을 씀(동적할당된 매개변수.) => <얕은 복사에 의한 문제..>
주소가 공유되어 소멸이 두번 일어남..
<해결 방법>
서로 힙공간을 따로 만들고 따로 해제시키기.
CName(const CName& rhs)
{
//m_pName = rhs.m_pName; //default복사생성자는 이런식으로 얕은복사를 해줘 메모리 해제 때 문제가 발생한다.
int iLength = strlen(rhs.m_pName) + 1;
m_pName = new char[iLength]; //이렇게 힙메모리에 별도의 저장공간을 만들고 거기에 내용을 복사해줘야 소멸자 문제가 발생하지 않음
strcpy_s(m_pName, iLength, rhs.m_pName);
}
//동적할당 따로따로 할당하기 때문에 문제가 일어나지 않음 => 깊은복사
<또다른 해결책들>
1.생성자 안에서 동적할당 안하면 해결될 문제긴 함
(동적할당을 생성자 아닌 시점에서 하면 얕은복사 문제가 일어나지 않음)
2.원본참조로 void Draw(CName& Name)해주면 끝남.
<extern>
쓰임) 전역변수 int g_iNumber= 10;를 클래스 멤버함수 안에서 전역변수를 출력하고싶을때
전역변수는 한 파일안에서만 유효함
<이를 가능하게 하려면>
똑같은 타입의 변수 이름을 player.cpp에서
extern int g_iNumer;
외부파일의 전역변수 이름을 사용할 수 있게 해주는 키워드.(존재를 알려주는 용도)
<한계>
extern -> 알려주기만 하지 용량을 할당하거나 하지는 않음
extern int g_iNumber ; => 메모리 용량이 없음.
특징) 원본이 없는 경우라면 얘 자체가 원본이 됨
원본 있는 경우라면 초기화식은 수행될 수 없음
보통 쓰는 방법) 헤더파일에다가 extern 선언해놓고 씀
함수도 extern 가능하지만 객체지향적 성격에는 맞지 않음.
(ex) API 창 구성하는 handle 을 extern으로 전역으로 선언함)
+ ) malloc으로 동적할당할 때 생길 수 있는 문제? => 생성자가 호출되지 않음.
'LANG' 카테고리의 다른 글
앞으로 무조건 만들 함수들 / 멤버 함수 구현 원칙 / 생성자와 소멸자 (0) | 2024.02.01 |
---|---|
피연산자/변수,상수/선언/자료형 (1) | 2024.01.05 |