1. 생성자의 초기화 리스트(initializer list) - [초기화 리스트: 생성자 호출과 동시에 멤버 변수들을 초기화, 생성과 동시에 초기화 되어야 하는 것]
2. static 변수(생성된 Marine 수 세기)
3. static 함수 - [static 함수 호출]
4. this - [레퍼런스를 리턴하는 함수]
5. 상수(const) 함수
+) ★복사생성자 호출 시점과 복사 생략★
1. 생성자의 초기화 리스트(initializer list)
이전 페이지의 Marine 클래스에서, 아래 코드를 추가해보자.
Marine::Marine() : hp(50), coordX(0), coordY(0), damage(5), isDead(false) {}
→ 생성자에서 무언가를 추가 도입한 것인데, 함수 본체에는 아무 것도 없는 상태로 볼 수 있다. 위 추가된 것들이 기존 생성자가 했던 일과 동일한 작업을 하고 있는 것이다.
기존의 생성자는 아래와 같은 역할을 했다.
Marine::Marine() {
hp = 50;
coordX = coordY = 0;
damage = 5;
isDead = false;
}
해당 생성자 내부에서 하던 멤버 변수들의 초기화 작업을 초기화 리스트가 대신해서 하고 있던 것이다.
초기화 리스트: 생성자 호출과 동시에 멤버 변수들을 초기화
→ Marine 클래스에서의 초기화 리스트
: coordX(x), coordY(y), hp(50), damage(5), isDead(false) {}
→ 일반적인 사용은 아래와 같다.
(생성자 이름) : var1(arg1), var2(arg2) {}
+) 왜 초기화 리스트를 사용할까?
1. 초기화 리스트를 사용 X: 생성 후, 대입을 수행한다. 디폴트 생성자가 호출된 뒤 대입이 수행된다.
Marine::Marine() {
hp = 50;
coordX = coordY = 0;
damage = 5;
isDead = false;
}
int a;
a = 10;
//와 비슷하다.
2. 초기화 리스트를 사용 O: 생성과 초기화를 동시에 진행한다. 복사 생성자가 호출된다.
Marine::Marine() : hp(50), coordX(0), coordY(0), damage(5), isDead(false) {}
int a = 10; //와 비슷하다.
→ 2.가 조금 더 효율적이라고 볼 수 있다.
생성과 동시에 초기화 되어야 하는 것
→ 대표적인 예로 레퍼런스(reference)와 상수(const)가 있다. 이 둘은 생성과 동시에 초기화가 되어야 한다. 여기서 만약 클래스 내부에 레퍼런스 변수나 상수를 넣고자 한다면 이들을 생성자에서 무조건 초기화 리스트를 사용해 초기화 해야 한다.
- 이렇게 만들었을 경우, 상수는 초기화 되는 순간 영원히 바뀌지 않게 된다.
Marine::Marine()
: hp(50), coordX(0), coordY(0), defaultDamage(5), isDead(false) {}
→ 다른 프로그래머가 실수로 marine의 default damage를 변경하는 명령을 집어 넣더라도, 컴파일 오류가 발생하게 되어, 효율적으로 오류를 발견할 수 있게 된다.
- 공격력을 외부에서 받는 생성자를 추가해보자.
#include <iostream>
class Marine {
int hp; // 마린 체력
int coordX, coordY; // 마린 위치
bool isDead;
const int defaultDamage; // 기본 공격력
public:
Marine(); // 기본 생성자
Marine(int x, int y); // x, y 좌표에 마린 생성
Marine(int x, int y, int defaultDamage);
int Attack(); // 데미지를 리턴한다.
void BeAttacked(int damageEarn); // 입는 데미지
void Move(int x, int y); // 새로운 위치
void ShowStatus(); // 상태를 보여준다.
};
Marine::Marine()
: hp(50), coordX(0), coordY(0), defaultDamage(5), isDead(false) {}
Marine::Marine(int x, int y)
: coordX(x), coordY(y), hp(50), defaultDamage(5), isDead(false) {}
Marine::Marine(int x, int y, int defaultDamage)
: coordX(x),
coordY(y),
hp(50),
defaultDamage(defaultDamage),
isDead(false) {}
void Marine::Move(int x, int y) {
coord_x = x;
coord_y = y;
}
int Marine::Attack() { return default_damage; }
void Marine::BeAttacked(int damage_earn) {
hp -= damageEarn;
if (hp <= 0) isDead = true;
}
void Marine::ShowStatus() {
std::cout << " *** Marine *** " << std::endl;
std::cout << " Location : ( " << coordX << " , " << coordY << " ) "
<< std::endl;
std::cout << " HP : " << hp << std::endl;
}
int main() {
Marine marine1(2, 3, 10);
Marine marine2(3, 5, 10);
marine1.ShowStatus();
marine2.ShowStatus();
std::cout << std::endl << "마린 1 이 마린 2 를 공격! " << std::endl;
marine2.BeAttacked(marine1.Attack());
marine1.ShowStatus();
marine2.ShowStatus();
}
위 코드에서 추가한 코드를 보자.
Marine::Marine(int x, int y, int defaultDamage)
: coordY(x),
coordX(y),
hp(50),
defaultDamage(defaultDamage),
// const int default_damage = (인자로 받은 default_damage);
isDead(false) {}
→ 이 부분 이전에는 defaultDamage에 초기화 리스트로 5를 전달했는데, 위 생성자의 경우 어떤 값을 전달할지를 인자로 받은 뒤, 상수를 넣었다.
defaultDamage(defaultDamage);
// const int default_damage = (인자로 받은 default_damage);
→ 즉, const int default_damage = (인자로 받은 default_damage);를 실행한 것과 마찬가지로 작동한다.
2. static 변수(생성된 Marine 수 세기)
- 생성된 Marine의 수를 세기 위한 단순한 방식
- 배열에 Marine을 보관해두고, 생성된 마린의 개수를 세기
- 어떤 변수를 만들어 Marine 생성 시, 1을 추가하고, 소멸 시 1을 뺀다.
→ 1.의 경우 (vector를 이용하면 쉽게 가능하다.) 크기가 변하는 배열을 따로 만들어야 하는 문제점이 있다. 2.의 경우, 어떤 함수 내에 Marine의 수에 대한 변수가 정의되어 있다면 그 값을 이용하기 위해 인자로 계속 전달해야 하는 귀찮음이 있다.
→ 전역 변수로 만든다면? 프로젝트 크기가 커질 수록 서로 겹쳐서 오류가 날 가능성이 많아서 반드시 필요한 경우가 아니라면 사용하지 않는다.
- static 멤버 변수
→ 전역변수 같지만, 클래스 하나에만 종속되는 변수인 static 멤버 변수를 사용할 수 있겠다. C언어에서 어떤 함수의 static 변수가 프로그램이 종료될 때 소멸했던 것처럼, 어떤 클래스의 static 멤버 변수의 경우, 멤버 변수들처럼 객체가 소멸될 때 소멸되는 것이 아니라, 프로그램이 종료될 때 소멸되게 된다.
ex) ★static 멤버 변수 예제★
#include <iostream>
class Marine {
static int totalMarineNum;
int hp; // 마린 체력
int coordX, coordY; // 마린 위치
bool isDead;
const int defaultDamage; // 기본 공격력
public:
Marine(); // 기본 생성자
Marine(int x, int y); // x, y 좌표에 마린 생성
Marine(int x, int y, int defaultDamage);
int Attack(); // 데미지를 리턴한다.
void BeAttacked(int damageEarn); // 입는 데미지
void Move(int x, int y); // 새로운 위치
void ShowStatus(); // 상태를 보여준다.
~Marine() { totalMarineNum--; }
};
int Marine::totalMarineNum = 0;
Marine::Marine()
: hp(50), coordX(0), coordY(0), defaultDamage(5), isDead(false) {
totalMarineNum++;
}
Marine::Marine(int x, int y)
: coordX(x), coordY(y), hp(50), defaultDamage(5), isDead(false) {
totalMarinenNum++;
}
Marine::Marine(int x, int y, int defaultDamage)
: coordX(x),
coordY(y),
hp(50),
defaultDamage(default_damage),
isDead(false) {
totalMarineNum++;
}
void Marine::Move(int x, int y) {
coordX = x;
coordY = y;
}
int Marine::Attack() { return defaultDamage; }
void Marine::BeAttacked(int damageEarn) {
hp -= damageEarn;
if (hp <= 0) isDead = true;
}
void Marine::ShowStatus() {
std::cout << " *** Marine *** " << std::endl;
std::cout << " Location : ( " << coordX << " , " << coordY << " ) "
<< std::endl;
std::cout << " HP : " << hp << std::endl;
std::cout << " 현재 총 마린 수 : " << totalMarineNum << std::endl;
}
void CreateMarine() {
Marine marine3(10, 10, 4);
marine3.ShowStatus();
}
int main() {
Marine marine1(2, 3, 5);
marine1.ShowStatus();
/*
*** Marine ***
Location : ( 2 , 3 )
HP : 50
현재 총 마린 수 : 1
*/
Marine marine2(3, 5, 10);
marine2.ShowStatus();
/*
*** Marine ***
Location : ( 3 , 5 )
HP : 50
현재 총 마린 수 : 2
*/
CreateMarine();
/*
*** Marine ***
Location : ( 10 , 10 )
HP : 50
현재 총 마린 수 : 3
*/
std::cout << std::endl << "마린 1 이 마린 2 를 공격! " << std::endl;
marine2.BeAttacked(marine1.Attack());
marine1.ShowStatus();
// 현재 총 마린 수: 2
marine2.ShowStatus();
// 현재 총 마린 수 : 2
}
→ 모든 전역 및 static 변수들은 정의와 동시에 값이 0으로 초기화되므로, 굳이 따로 초기화하지 않아도 클래스 static 변수들은 아래와 같이 초기화된다.
int Marine::totalMarineNum = 0;
와 같이 초기화해도 되지 않은지를 묻는 경우가 있는데, 멤버 변수들을 위와 같이 초기화시키지 못하는 것처럼 static 변수 역시 클래스 내부에서 위와 같이 초기화 하는 것은 불가능하다.
아래와 같은 꼴이 되는 유일한 경우는 const static 변수일 때이다. 컴파일러가 해당 값을 아예 상수로 대체해버릴 수 있어서 예외적으로 허용된다.
class Marine {
const static int x = 0;
- 생성자/소멸자 그리고 static 변수 활용
클래스는 생성자와 소멸자를 제공한다는 점에서 편하게 사용이 가능하다. 덕분에 Marine이 생성될 때, 그리고 소멸될 때 굳이 따로 처리하지 않고, 생성자/소멸자 안에 totalMarineNum을 조작하는 문장을 넣어 편하게 처리할 수 있다.
Marine::Marine()
: hp(50), coordX(0), coordY(0), defaultDamage(5), isDead(false) {
totalMarineNum++;
}
Marine::Marine(int x, int y)
: coordX(x), coordY(y), hp(50), defaultDamage(5), isDead(false) {
totalMarineNum++;
}
Marine::Marine(int x, int y, int defaultDamage)
: coordX(x),
coordY(y),
hp(50),
defaultDamage(defaultDamage),
isDead(false) {
totalMarineNum++;
}
이런 식으로, 각 생성자를 호출할 때, totalMarineNum을 1씩 증가하도록 했다. 그리고 소멸 될 때는 아래와 같이 1씩 감소시키는 코드를 작성하면 된다.
~Marine() { totalMarineNum--; }
그리고, 위에서 있던 createMarine()을 보자.
void CreateMarine() {
Marine marine3(10, 10, 4);
marine3.ShowStatus();
}
→ marine3을 생성함으로써 총 marine의 수가 3이 되는 것을 확인할 수 있다. marine3은 createMarine의 지역 객체로, 해당 함수가 종료될 때 소멸하게 된다. 따라서, createMarine() 이 후, main 함수에서 확인한 객체의 수는 2가 되는 것을 알 수 있다.
3. static 함수
→ static 변수가 어떠한 객체에 종속되는 것이 아니라, 그냥 클래스 자체에 딱 1개 존재하는 것처럼 static 함수 역시 어떤 특정 객체에 종속되는 것이 아니라, 클래스 전체에 딱 1개 존재하는 함수이다.
static이 아닌 멤버 함수들의 경우, 객체를 만들어야 각 멤버 함수들을 호추할 수 있지만, static 함수는 객체가 없어도 클래스 자체에서 호출할 수 있다.
ex) static 함수 예제
#include <iostream>
class Marine {
static int totalMarineNum;
const static int i = 0;
int hp;
int coordX, coordY;
bool isDead;
const int defaultDamage;
public:
Marine();
Marine(int x, int y);
Marine(int x, int y, int defaultDamage);
int Attack();
void BeAttacked(int damageEarn);
void Move(int x, int y);
void ShowStatus();
static void ShowTotalMarine();
~Marine() { totalMarineNum--; }
};
// static 멤버 변수 정의
int Marine::totalMarineNum = 0;
void Marine::ShowTotalMarine() {
std::cout << "전체 마린 수 : " << totalMarineNum << std::endl;
}
Marine::Marine()
: hp(50), coordX(0), coordY(0), defaultDamage(5), isDead(false) {
totalMarineNum++;
}
Marine::Marine(int x, int y)
: coordX(x), coordY(y), hp(50), defaultDamage(5), isDead(false) {
totalMarineNum++;
}
Marine::Marine(int x, int y, int defaultDamage)
: coordX(x), coordY(y), hp(50), defaultDamage(defaultDamage), isDead(false) {
totalMarineNum++;
}
void Marine::Move(int x, int y) {
coordX = x;
coordY = y;
}
int Marine::Attack() {
return defaultDamage;
}
void Marine::BeAttacked(int damageEarn) {
hp -= damageEarn;
if (hp <= 0) isDead = true;
}
void Marine::ShowStatus() {
std::cout << " *** Marine *** " << std::endl;
std::cout << " Location : ( " << coordX << " , " << coordY << " ) " << std::endl;
std::cout << " HP : " << hp << std::endl;
std::cout << " 현재 총 마린 수 : " << totalMarineNum << std::endl;
}
void CreateMarine() {
Marine marine3(10, 10, 4);
Marine::ShowTotalMarine();
}
int main() {
Marine marine1(2, 3, 5);
Marine::ShowTotalMarine();
Marine marine2(3, 5, 10);
Marine::ShowTotalMarine();
CreateMarine();
std::cout << std::endl << "마린 1 이 마린 2 를 공격! " << std::endl;
marine2.BeAttacked(marine1.Attack());
marine1.ShowStatus();
marine2.ShowStatus();
return 0;
}
→ static 함수는 특정 객체에 종속되는 것이 아니라, 클래스에 종속된다.
static 함수 호출
따라서, 위 코드에서 봤듯, static 함수를 호출하는 방법은 바로 아래의 방법이 아니다.
(객체).(멤버함수) //가 아니다.
- static 함수 호출 방법
(클래스)::(static 함수) //이렇게 호출한다.
ex) static 함수 호출 예제
Marine::ShowTotalMarine();
왜 위와 같이 호출하냐면 어떤 객체도 이 함수를 소유하고 있지 않기 때문이다. 그래서, static 함수 내에서는 클래스의 static 변수만을 이용할 수 밖에 없다.
ex) 만약 static 함수 내에서 아래와 그냥 클래스의 멤버변수를 이용한다면? (error)
void Marine::ShowTotalMarine() {
std::cout << defaultDamage << std::endl; // default_damage 는 멤버 변수
std::cout << "전체 마린 수 : " << totalMarineNum << std::endl;
}
→ defaultDamage가 어떤 객체의 defaultDamage 인지 아무도 모르는 상황이 발생한다. 즉, 어떤 객체의 defaultDamage인지 static 함수인 ShowTotalMarine()은 알 수 없다. 해당 함수는 어떤 객체에도 속해있지 않기 때문이다.
4. this
→ 자기 자신을 가리키는 포인터의 역할을 하는 this
- 레퍼런스를 리턴하는 함수와 this
Marine& Marine::BeAttacked(int damageEarn) {
hp -= damageEarn;
if (hp <= 0) isDead = true;
return *this;
}
→ 위 내용은 아래와 동일한 의미가 된다(구조체 포인터 변수에서 ->를 통해 구조체 원소들에 접근했던 것을 생각해보자.).
Marine& Marine::BeAttacked(int damageEarn) {
this->hp -= damageEarn;
if (this->hp <= 0) this->isDead = true;
return *this;
}
실제로 모든 멤버 함수 내에서는 this 키워드가 정의되어 있고, 클래스 안에서 정의된 함수 중에서 this 키워드가 없는 함수는 당연히, static 함수 뿐이다.
+) Marine&을 리턴한다는 말이 무슨 말일까? 레퍼런스는 어떤 변수의 다른 별명(alias)라고도 했었다. 그런데 그 별명을 리턴한다는 것은 무슨 말일까?
레퍼런스를 리턴하는 함수
#include <iostream>
class A {
int x;
public:
A(int c) : x(c) {}
int& AccessX() { return x; }
int GetX() { return x; }
void ShowX() { std::cout << x << std::endl; }
};
int main() {
A a(5);
a.ShowX(); //5
int& c = a.AccessX();
c = 4;
a.ShowX(); //4
int d = a.AccessX();
d = 3;
a.ShowX(); //4
// 아래는 오류
// int& e = a.GetX();
// e = 2;
// a.ShowX();
int f = a.GetX();
f = 1;
a.ShowX(); //4
}
→ 주석을 달았듯 5, 4, 4, 4 가 나오게 된다. 여기서 Class A의 두 함수를 비교해보자.
- class A의 AccessX() 함수와 GetX() 함수에 대한 비교
AccessX() 함수는 x의 '레퍼런스'를 리턴하고, GetX() 함수는 x의 ‘값’을 리턴한다.
1.
int& c = a.AccessX();
c = 4;
a.ShowX();
→ 레퍼런스 c는 ‘x의 레퍼런스’, 즉, x의 별명을 받은 꼴이다. int &c = x;와 같다고 볼 수 있는 것이다. 따라서, c의 값을 바꿔 a의 x 값을 바꾼 것이 된다.
2.
int d = a.AccessX();
d = 3;
a.ShowX();
→ int&가 아닌 int형 변수에 x의 별명을 전달했다. d가 그냥 int형 변수이므로, 값의 복사가 일어나 d에는 x의 값이 들어가게 된다. d는 x의 별명이 아닌 다른 독립적인 변수로, d의 값을 변경해도 a 객체의 x의 값은 바뀌지 않는다.
3.
// int& e = a.GetX();
// e = 2;
// a.ShowX();
→ 레퍼런스가 아닌 타입을 return 하면 값의 복사가 이루어져 임시객체가 생성되는데, 임시객체의 레퍼런스를 가질 수는 없기 때문이다. 임시객체는 문장이 끝나면 소멸된다. 즉, GetX()를 통해 생성된 값은 임시객체에 존재하는데, 해당 임시객체는 레퍼런스를 가질 수 없어서, int& 타입의 e에는 들어갈 수 없다.
4.
int f = a.GetX();
f = 1;
a.ShowX();
→ 임시로 생성된 int 변수가 f에 복사되는데, 그냥 f = 1을 한 것으로, 실제 객체 a의 x에는 아무런 영향을 끼치지는 않는다.
- Marine에서의 레퍼런스 리턴 함수
Marine& Marine::BeAttacked(int damageEarn) {
this->hp -= damageEarn;
if (this->hp <= 0) this->isDead = true;
return *this;
}
→ BeAttacked는 Marine& 타입을 리턴하는데, 위 경우 *this를 리턴한다(+) this는 이 함수를 호출한 객체를 가리키는 것이다. 즉, *this는 객체 자신을 의미한다.)
marine2.BeAttacked(marine1.Attack()).BeAttacked(marine1.Attack());
→ 앞에 있는 marine2.BeAttacked(marine1.Attack())이 먼저 실행되고, 리턴되는 것이 다시 marine2이므로, 다음에 marine2.BeAttacked(marine1.Attack()이 실행되는 것으로 생각할 수 있다.
- 여기서 만약, 아래와 같이 BeAttacked 함수의 리턴 타입이 Marine이 되었다면?
Marine::BeAttacked(int damageEarn) {
this->hp -= damageEarn;
if (this->hp <= 0) this->isDead = true;
return *this;
}
- 그리고 위에 대한 함수로 marine2.BeAttacked(marine1.Attack()).BeAttacked(marine1.Attack());을 실행해보면?
+) *this는 현재 객체를 가리키는 “자기 자신”이다.
→ marine2는 실제로 2번 공격이 아닌 1번 공격으로 감소한 HP를 보인다. 40이 아닌 45가 나온다는 것이다. return 타입이 Marine이기 때문에, 임시 객체 Marine을 생성해 *this의 내용으로 복사(Marine의 복사생성자 호출)가 되고, 해당 임시 객체에 대한 BeAttacked 함수가 호출되게 되는 것이다.
따라서, 두 번째 BeAttacked는 marine2가 아닌 다른 엉뚱한 임시 객체에 대해 호출되는 것이 되고, marine2는 marine1 의 공격을 1번만 받게 되는 것으로 된다.
→ 즉, 리턴 타입을 &Marine 으로 해야 참조가 유지되고, 원본 객체에 적용되는 꼴이 된다. Marine으로 리턴 타입을 두면, 원본이 아닌 복사본(임시 객체에 저장)이 사용된다.
5. 상수(const)함수
→ 변수들의 값을 바꾸지 않고 읽기만 하는 상수 같은 멤버함수를 상수함수로 선언할 수 있다.
- 상수함수 선언 방법
(기존의 함수의 정의) const;
위에서 사용했던 예제에서 Attack() 함수만 수정해보자. 함수의 정의 역시 const 키워드를 꼭 넣어야 한다.
int Marine::Attack() const { return defaultDamage; }
→ Attack() 함수는 상수 멤버함수로 정의된 것이다. 상수함수로 이 함수를 정의하여 다른 변수의 값을 바꾸지 않는 함수라고 다른 프로그래머에게 명시할 수 있다. 사웃함수 내에서는 객체들의 읽기만이 수행되고, 상수함수 내에서 호출할 수 있는 함수는 다른 상수함수 뿐이다.
Tip) 일반적인 사용
많은 경우에 클래스를 설계할 때, 멤버변수들은 모두 private에 넣고, 이 변수들의 값에 접근하는 방법으로 GetX() 함수처럼 함수를 public에 넣어 이 함수를 이용해 값을 return 받는 형식으로 많이 사용한다.
→ 이런 식으로 멤버 변수들을 private에 넣음으로써 함부로 변수에 접근하는 것을 막고, 그 값을 자유롭게 구할 수 있게 된다.
+) 복사생성자 호출 시점과 복사 생략
+) ★생각해볼 문제(불필요한 복사 방지): 복사 생성이 몇 번 일어날까?★
#include <iostream>
class A {
int x;
public:
A(int c) : x(c) {}
A(const A& a) {
x = a.x;
std::cout << "복사 생성" << std::endl;
}
};
class B {
A a;
public: // 외부에서 접근 가능한 함수나 변수 위치
// B 생성자로, 내부 멤버 A도 생성된다.
B(int c) : a(c) {}
// B의 복사 생성자로 내부 A도 복사된다.
B(const B& b) : a(b.a) {}
// 캡슐화를 유지하면서 내부 멤버(a)를 안전하게 복사하여
// 외부에 제공하기 위한 아래 코드
// 리턴 타입이 A인 GetA() 함수
A GetA() {
// temp라는 `A 타입 객체`에 `멤버변수 a`를 복사해서(복사생성자 호출)
// 복사 생성 1회(확정)
A temp(a);
// return 타입에 맞게 return 한다.
// 복사가 1회 더 일어날까? → C++에서는 함수 밖으로 값을 반환 시,
// 값 그대로 반환하는 것이 아니라, 값을 복사해서 함수 밖으로
// 내보내는 방식이다.
return temp;
// A result = temp;(== A result(temp);) 같은 꼴이다.
// 이건 temp를 return 값의 공간에 복사하는 꼴로
// 복사 생성자가 작동하는 것으로 볼 수도 있다.
// +) 디폴트 생성자가 호출되는 것으로 착각하지 말자. 디폴트 생성자는
// 값 없이 객체만 만들 때 호출된다. (ex) A result;)
}
};
int main() {
// b 객체를 통해 만들어진 A 클래스 타입의 객체인 a에 있는
// x 멤버 변수에 10을 넣은 것
B b(10);
std::cout << "---------" << std::endl;
// b.GetA()가 실행될 때의 temp는 b.GetA() 함수
// 안에서만 존재하는 지역변수이다.
A a1 = b.GetA();
}
→ 정답은 1) 컴파일러가 복사 생략을 지원한다면 '1번', 2) 아니면 '2번'이 될 것이다.
위 내용 요약)
B b(10); // 생성자 호출 → A 객체 생성
A a1 = b.GetA(); // GetA() 내부에서 A temp ← 복사 생성자 호출
// return 시 복사 1번 더 가능 (대부분 생략)
+) x에 10이 들어있는 A 타입 객체 a를 복사해서 → A 타입의 temp를 만들고 → 그걸 다시 복사해서 A 타입 변수 a1에 대입한 꼴이다.
+) 복사생성자에서 참조자를 사용하는 이유?
→ 참조자로 받지 않으면, 복사생성자 호출 시 또 복사가 되는 등의 일이 발생할 수 있다. 추가로, 복사하면서 원본 객체를 변경하면 안돼서, const를 붙인다. 즉, 원본 객체를 효율적이고, 안전하게 참조하기 위한 것이다.
'공부 > C++' 카테고리의 다른 글
[C++] 복사생성자(Copy Constructor), 소멸자(Destructor) (1) | 2025.03.21 |
---|---|
[C++] 객체지향, 오버로딩(Overloading), 생성자(Constructor) (0) | 2025.03.20 |
[C++] Static, new/delete, Naming Rules (0) | 2025.03.16 |