[2026] C++ Copy & Move Constructors: Rule of Five, RAII, and noexcept
이 글의 핵심
Rule of Five in C++: copy/move constructors and assignment, deep copy vs shallow, self-assignment, noexcept moves, copy elision, and FileHandle patterns.
Rule of Five
Types that manage resources should define the five special members (constructor, destructor, copy ctor, copy assign, move ctor, move assign—often counted as five “operations” plus destructor). 다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class Resource {
private:
int* data;
size_t size;
public:
Resource(size_t s) : size(s), data(new int[s]) {}
~Resource() {
delete[] data;
}
Resource(const Resource& other)
: size(other.size), data(new int[other.size]) {
copy(other.data, other.data + size, data);
}
Resource& operator=(const Resource& other) {
if (this != &other) {
delete[] data;
size = other.size;
data = new int[size];
copy(other.data, other.data + size, data);
}
return *this;
}
Resource(Resource&& other) noexcept
: size(other.size), data(other.data) {
other.data = nullptr;
other.size = 0;
}
Resource& operator=(Resource&& other) noexcept {
if (this != &other) {
delete[] data;
data = other.data;
size = other.size;
other.data = nullptr;
other.size = 0;
}
return *this;
}
};
Copy constructor
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class String {
private:
char* data;
size_t length;
public:
String(const char* str) {
length = strlen(str);
data = new char[length + 1];
strcpy(data, str);
}
String(const String& other) {
length = other.length;
data = new char[length + 1];
strcpy(data, other.data);
cout << "copy ctor" << endl;
}
~String() {
delete[] data;
}
};
int main() {
String s1("Hello");
String s2 = s1; // copy ctor
String s3(s1); // copy ctor
}
Move constructor
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class String {
private:
char* data;
size_t length;
public:
String(const char* str) {
length = strlen(str);
data = new char[length + 1];
strcpy(data, str);
}
String(String&& other) noexcept {
data = other.data;
length = other.length;
other.data = nullptr;
other.length = 0;
cout << "move ctor" << endl;
}
~String() {
delete[] data;
}
};
int main() {
String s1("Hello");
String s2 = move(s1); // move ctor
}
Practical examples
Example 1: Dynamic array
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
template<typename T>
class DynamicArray {
private:
T* data;
size_t size;
size_t capacity;
public:
DynamicArray(size_t cap = 10)
: size(0), capacity(cap), data(new T[cap]) {}
~DynamicArray() {
delete[] data;
}
DynamicArray(const DynamicArray& other)
: size(other.size), capacity(other.capacity),
data(new T[other.capacity]) {
copy(other.data, other.data + size, data);
}
DynamicArray& operator=(const DynamicArray& other) {
if (this != &other) {
delete[] data;
size = other.size;
capacity = other.capacity;
data = new T[capacity];
copy(other.data, other.data + size, data);
}
return *this;
}
DynamicArray(DynamicArray&& other) noexcept
: size(other.size), capacity(other.capacity), data(other.data) {
other.data = nullptr;
other.size = 0;
other.capacity = 0;
}
DynamicArray& operator=(DynamicArray&& other) noexcept {
if (this != &other) {
delete[] data;
data = other.data;
size = other.size;
capacity = other.capacity;
other.data = nullptr;
other.size = 0;
other.capacity = 0;
}
return *this;
}
void push_back(const T& value) {
if (size == capacity) {
resize();
}
data[size++] = value;
}
T& operator {
return data[index];
}
size_t getSize() const {
return size;
}
private:
void resize() {
capacity *= 2;
T* newData = new T[capacity];
copy(data, data + size, newData);
delete[] data;
data = newData;
}
};
int main() {
DynamicArray<int> arr1;
arr1.push_back(1);
arr1.push_back(2);
DynamicArray<int> arr2 = arr1; // copy
DynamicArray<int> arr3 = move(arr1); // move
}
Example 2: File handle
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class FileHandle {
private:
FILE* file;
string filename;
public:
FileHandle(const string& name) : filename(name) {
file = fopen(name.c_str(), "r");
if (!file) {
throw runtime_error("failed to open file");
}
}
~FileHandle() {
if (file) {
fclose(file);
}
}
FileHandle(const FileHandle&) = delete;
FileHandle& operator=(const FileHandle&) = delete;
FileHandle(FileHandle&& other) noexcept
: file(other.file), filename(move(other.filename)) {
other.file = nullptr;
}
FileHandle& operator=(FileHandle&& other) noexcept {
if (this != &other) {
if (file) {
fclose(file);
}
file = other.file;
filename = move(other.filename);
other.file = nullptr;
}
return *this;
}
string read() {
if (!file) return "";
fseek(file, 0, SEEK_END);
long size = ftell(file);
fseek(file, 0, SEEK_SET);
string content(size, '\0');
fread(&content[0], 1, size, file);
return content;
}
};
int main() {
FileHandle fh1("test.txt");
// FileHandle fh2 = fh1; // error: copy deleted
FileHandle fh2 = move(fh1); // OK
}
Example 3: Tiny unique_ptr-like type
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
template<typename T>
class UniquePtr {
private:
T* ptr;
public:
explicit UniquePtr(T* p = nullptr) : ptr(p) {}
~UniquePtr() {
delete ptr;
}
UniquePtr(const UniquePtr&) = delete;
UniquePtr& operator=(const UniquePtr&) = delete;
UniquePtr(UniquePtr&& other) noexcept : ptr(other.ptr) {
other.ptr = nullptr;
}
UniquePtr& operator=(UniquePtr&& other) noexcept {
if (this != &other) {
delete ptr;
ptr = other.ptr;
other.ptr = nullptr;
}
return *this;
}
T& operator*() const {
return *ptr;
}
T* operator->() const {
return ptr;
}
T* get() const {
return ptr;
}
T* release() {
T* temp = ptr;
ptr = nullptr;
return temp;
}
};
int main() {
UniquePtr<int> p1(new int(42));
// UniquePtr<int> p2 = p1; // error
UniquePtr<int> p2 = move(p1); // OK
cout << *p2 << endl; // 42
}
Copy elision
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
String createString() {
return String("Hello"); // may elide copy/move
}
int main() {
String s = createString(); // often no copy/move (C++17 guaranteed in many cases)
}
Common pitfalls
Pitfall 1: Shallow copy
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// Bad: shallow copy (default copy ctor)
class BadString {
private:
char* data;
public:
BadString(const char* str) {
data = new char[strlen(str) + 1];
strcpy(data, str);
}
~BadString() {
delete[] data;
}
};
BadString s1("Hello");
BadString s2 = s1; // same pointer
// double free on destruction
// Good: deep copy
class GoodString {
private:
char* data;
public:
GoodString(const char* str) {
data = new char[strlen(str) + 1];
strcpy(data, str);
}
GoodString(const GoodString& other) {
data = new char[strlen(other.data) + 1];
strcpy(data, other.data);
}
~GoodString() {
delete[] data;
}
};
Pitfall 2: Self-assignment
다음은 cpp를 활용한 상세한 구현 코드입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// Bad: no self-check
Resource& operator=(const Resource& other) {
delete[] data; // breaks on self-assign
size = other.size;
data = new int[size];
copy(other.data, other.data + size, data);
return *this;
}
// Good
Resource& operator=(const Resource& other) {
if (this != &other) {
delete[] data;
size = other.size;
data = new int[size];
copy(other.data, other.data + size, data);
}
return *this;
}
Pitfall 3: Missing noexcept
아래 코드는 cpp를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// Without noexcept
Resource(Resource&& other) {
// ...
}
// With noexcept
Resource(Resource&& other) noexcept {
// ...
}
// Reason: std::vector prefers noexcept moves when reallocating
FAQ
Q1: When is Rule of Five needed?
A: When you directly manage resources (memory, handles, etc.).
Q2: Copy vs move?
A:
- Copy: independent duplicate
- Move: transfer ownership—usually faster
Q3: = delete?
A: To forbid copy/move—like unique_ptr.
Q4: Why noexcept?
A: Exception safety and container optimizations.
Q5: Rule of Zero?
A: Use smart pointers and RAII wrappers—often no manual special members.
Q6: Learning resources?
A:
- Effective Modern C++
- cppreference.com
- C++ Primer
Related posts (internal links)
Practical tips
Debugging
- Warnings first
Performance
- Profile
Code review
- Conventions
Practical checklist
Before coding
- Right approach?
- Maintainable?
- Performance?
While coding
- Warnings?
- Edge cases?
- Errors?
At review
- Intent?
- Tests?
- Docs?