문제 상황을 명확히 보여주는 간단한 예제 코드를 살펴보자.
핵심 테스트 코드
int main()
{
cout << "1."; Bar b1; // 기본 생성자
cout << "2."; Bar b2(b1); // 복사 생성자(직접 초기화)
cout << "3."; Bar b3 = b1; // 복사 생성자(복사 초기화)
cout << "4."; Bar b4 = static_cast<Bar>(10); // 기본 최적화: 직접 생성; 최적화 비활성화 시 직접 생성+이동 생성(복사 초기화)
cout << "5."; b1 = b2; // 복사 대입
cout << "6."; b1 = static_cast<Bar>(5); // 직접 생성, 이동 대입, 우변 이동 후 객체 즉시 소멸
cout << "----------" << endl;
}
실행 결과 및 분석
# g++ main.cpp && ./a.out
1.기본 생성 ptr = 0 # b1
2.복사 생성 ptr = 0x55a4e8cb0280 # b2
3.복사 생성 ptr = 0x55a4e8cb02a0 # b3
4.직접 생성 ptr = 0x55a4e8cb02c0 # b4
5.복사 대입 ptr = 0x55a4e8cb02f0 # b1=b2, deep copy로 새로운 ptr 주소 생성
6.직접 생성 ptr = 0x55a4e8cb0310 # static_cast<Bar>(5)
이동 대입 ptr = 0x55a4e8cb0310 # static_cast<Bar>(5) 가 b1으로 이전됨
소멸 ptr = 0 # 이전 후 static_cast<Bar>(5) 소멸
----------
소멸 ptr = 0x55a4e8cb02c0 # b4
소멸 ptr = 0x55a4e8cb02a0 # b3
소멸 ptr = 0x55a4e8cb0280 # b2
소멸 ptr = 0x55a4e8cb0310 # b1
결론
- 직접 초기화와 복사 초기화 모두복사 생성자를 호출한다.
- 우측값(将亡值)인 테스트 코드의
static_cast<Bar>(5)는 즉시 소멸된다. - 지역 변수 소멸 순서(b4,b3,b2,b1)는 정의 순서(b1,b2,b3,b4)와 반대로 진행된다.
- 기본 최적화 기능을 사용하면 생성+이동을 건너뛰고 임시 변수를 생략하여 b4 객체를 직접 생성할 수 있다. 즉,
Bar b4 = static_cast<Bar>(10);는 기본적으로Bar b4(10);으로 최적화된다. cppInsight에서도 동일한 결과를 확인할 수 있다: 기본 상태에서string s1(3, 't');와string s2 = string(3,'t');가 생성하는 코드가 완전히 동일하다 https://cppinsights.io/s/161fe366
완전한 테스트 코드
#include <iostream>
using namespace std;
class Bar {
public:
Bar() : len(0), data(nullptr) { cout << "기본 생성 ptr = " << data << endl; }
explicit Bar(unsigned n) : len(n)
{
data = new int[len];
cout << "직접 생성 ptr = " << data << endl;
}
Bar(const Bar& src) : len(src.len)
{
data = new int[len];
for (unsigned i = 0; i < len; ++i) data[i] = src.data[i];
cout << "복사 생성 ptr = " << data << endl;
}
Bar(Bar&& src) : len(src.len)
{
data = src.data;
src.data = nullptr;
cout << "이동 생성 ptr = " << data << endl;
}
Bar& operator=(const Bar& src)
{ // 메모리 누수 발생! 표준 해결법: 복사 후 스왑
if (this == &src) return *this;
len = src.len;
data = new int[len];
for (unsigned i = 0; i < len; ++i) data[i] = src.data[i];
cout << "복사 대입 ptr = " << data << endl;
return *this;
}
Bar& operator=(Bar&& src)
{
if (this == &src) return *this;
len = src.len;
data = src.data;
src.data = nullptr;
cout << "이동 대입 ptr = " << data << endl;
return *this;
}
~Bar()
{
cout << "소멸 ptr = " << data << endl;
if (data) {
delete[] data;
data = nullptr;
}
}
private:
unsigned len;
int* data;
};
int main()
{
cout << "1."; Bar b1; // 기본 생성자
cout << "2."; Bar b2(b1); // 복사 생성자(직접 초기화)
cout << "3."; Bar b3 = b1; // 복사 생성자(복사 초기화)
cout << "4."; Bar b4 = static_cast<Bar>(10); // 기본 최적화: 직접 생성; 최적화 비활성화 시 직접 생성+이동 생성
cout << "5."; b1 = b2; // 복사 대입
cout << "6."; b1 = static_cast<Bar>(5); // 직접 생성, 이동 대입, 즉시 소멸
cout << "----------" << endl;
}