[C++] Constructor, Copy/Move Semantics

choohaยท2026๋…„ 1์›” 3์ผ

C++

๋ชฉ๋ก ๋ณด๊ธฐ
12/22

๐Ÿ“š C++ ์ƒ์„ฑ์ž์™€ ๋ณต์‚ฌ/์ด๋™ semantics

๐Ÿ”ธ Member Initializer List (๋ฉค๋ฒ„ ์ดˆ๊ธฐํ™” ๋ฆฌ์ŠคํŠธ)

๋ฌธ์ œ๊ฐ€ ๋˜๋Š” ์ฝ”๋“œ

class Cat
{
public:
    Cat() { mAge = 1; }
    Cat(int age) { mAge = age; }
private:
    int mAge;
};

class Zoo
{
public:
    Zoo(int kittyAge) { mKitty = Cat(kittyAge); }  // โŒ ๋น„ํšจ์œจ์ !
private:
    Cat mKitty;
};

int main()
{
    Zoo cppZoo(4);
    return 0;
}

๋ฌธ์ œ์ : ๋ถˆํ•„์š”ํ•œ ์ž„์‹œ ๊ฐ์ฒด ์ƒ์„ฑ
1. Zoo ์ƒ์„ฑ์ž๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ์ „์— mKitty์˜ ๊ธฐ๋ณธ ์ƒ์„ฑ์ž Cat()๊ฐ€ ๋จผ์ € ํ˜ธ์ถœ๋จ
2. ์ƒ์„ฑ์ž ๋ณธ๋ฌธ์—์„œ Cat(kittyAge)๋กœ ์ž„์‹œ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ
3. ์ž„์‹œ ๊ฐ์ฒด๋ฅผ mKitty์— ๋Œ€์ž… (๋ณต์‚ฌ ๋˜๋Š” ์ด๋™)
4. ์ž„์‹œ ๊ฐ์ฒด ์†Œ๋ฉธ

โ†’ ์ด 2๋ฒˆ์˜ ์ƒ์„ฑ์ž ํ˜ธ์ถœ + 1๋ฒˆ์˜ ๋Œ€์ž… ์—ฐ์‚ฐ ๋ฐœ์ƒ

Member Initializer List๋กœ ํ•ด๊ฒฐ

class Zoo
{
public:
    Zoo(int kittyAge) : mKitty{kittyAge} {}  // โœ… ํšจ์œจ์ !
private:
    Cat mKitty;
};

์žฅ์ :

  • mKitty๋ฅผ ์ƒ์„ฑ๊ณผ ๋™์‹œ์— ์ดˆ๊ธฐํ™”
  • ๋ถˆํ•„์š”ํ•œ ๊ธฐ๋ณธ ์ƒ์„ฑ์ž ํ˜ธ์ถœ ์ œ๊ฑฐ
  • ์ž„์‹œ ๊ฐ์ฒด ์ƒ์„ฑ ์—†์Œ
  • ๋‹จ 1๋ฒˆ์˜ ์ƒ์„ฑ์ž ํ˜ธ์ถœ๋งŒ ๋ฐœ์ƒ

Member Initializer List ์‚ฌ์šฉ ๊ทœ์น™

  • () ๋Œ€์‹  {}๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋” ์•ˆ์ „ (narrowing conversion ๋ฐฉ์ง€)
  • ๋ฉค๋ฒ„ ๋ณ€์ˆ˜ ์„ ์–ธ ์ˆœ์„œ๋Œ€๋กœ ์ดˆ๊ธฐํ™”๋จ (์ดˆ๊ธฐํ™” ๋ฆฌ์ŠคํŠธ ์ˆœ์„œ์™€ ๋ฌด๊ด€)
  • const ๋ฉค๋ฒ„ ๋ณ€์ˆ˜์™€ ์ฐธ์กฐ ๋ฉค๋ฒ„ ๋ณ€์ˆ˜๋Š” ๋ฐ˜๋“œ์‹œ ์ดˆ๊ธฐํ™” ๋ฆฌ์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•จ
class Example
{
public:
    Example(int a, int b, int c) 
        : mA{a}           // ๋ฉค๋ฒ„ ๋ณ€์ˆ˜
        , mB{b}           // ์ฝค๋งˆ๋กœ ๊ตฌ๋ถ„
        , mC{c} {}        // ๋งˆ์ง€๋ง‰๋„ ์ฝค๋งˆ ๊ฐ€๋Šฅ
        
private:
    int mA;
    int mB;
    int mC;
};

๐Ÿ”ธ Copy/Move Constructor & Assignment

๊ธฐ๋ณธ ์˜ˆ์ œ ์ฝ”๋“œ

class Cat
{
public:
    Cat() = default;  // ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๋Š” ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ž๋™ ์ƒ์„ฑ
    
    Cat(std::string name, int age) 
        : mName{std::move(name)}
        , mAge{age}
    {
        std::cout << mName << " constructor" << std::endl;
    }
    
    ~Cat()
    {
        std::cout << mName << " destructor" << std::endl;
    }
    
    // Copy Constructor (๋ณต์‚ฌ ์ƒ์„ฑ์ž)
    Cat(const Cat& other) 
        : mName{other.mName}
        , mAge{other.mAge}
    {
        std::cout << mName << " copy constructor" << std::endl;
    }
    
    // Move Constructor (์ด๋™ ์ƒ์„ฑ์ž)
    Cat(Cat&& other) noexcept
        : mName{std::move(other.mName)}
        , mAge{other.mAge}
    {
        std::cout << mName << " move constructor" << std::endl;
    }
    
    // Copy Assignment (๋ณต์‚ฌ ๋Œ€์ž… ์—ฐ์‚ฐ์ž)
    Cat& operator=(const Cat& other)
    {
        if (&other == this) return *this;  // Self-assignment ์ฒดํฌ
        
        mName = other.mName;
        mAge = other.mAge;
        std::cout << mName << " copy assignment" << std::endl;
        return *this;
    }
    
    // Move Assignment (์ด๋™ ๋Œ€์ž… ์—ฐ์‚ฐ์ž)
    Cat& operator=(Cat&& other) noexcept
    {
        if (&other == this) return *this;  // Self-assignment ์ฒดํฌ
        
        mName = std::move(other.mName);
        mAge = other.mAge;
        std::cout << mName << " move assignment" << std::endl;
        return *this;
    }
    
    void print() const
    {
        std::cout << mName << " " << mAge << std::endl;
    }
    
private:
    std::string mName;
    int mAge;
};

์‚ฌ์šฉ ์˜ˆ์ œ

int main()
{
    Cat kitty{"kitty", 1};              // ์ผ๋ฐ˜ ์ƒ์„ฑ์ž
    Cat kitty2{kitty};                  // ๋ณต์‚ฌ ์ƒ์„ฑ์ž
    Cat kitty3{std::move(kitty)};       // ์ด๋™ ์ƒ์„ฑ์ž
    
    Cat bori{"bori", 1};
    Cat nabi{"nabi", 2};
    
    //bori = nabi;						// ๋ณต์‚ฌ ๋Œ€์ž… ์—ฐ์‚ฐ์ž
    bori = std::move(nabi);             // ์ด๋™ ๋Œ€์ž… ์—ฐ์‚ฐ์ž
    
    bori.print();  // nabi 2 (nabi์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ด)
    nabi.print();  // (empty) 2 (mName์ด ์ด๋™๋˜์–ด ๋น„์–ด์žˆ์Œ)
    
    return 0;
}
  • ์ผ๋ฐ˜/๋ณต์‚ฌ/์ด๋™ ์ƒ์„ฑ์ž
  • ๋ณต์‚ฌ/์ด๋™ ๋Œ€์ž… ์—ฐ์‚ฐ์ž

๐Ÿ”ธ Self-Assignment ๋ฌธ์ œ์™€ ํ•ด๊ฒฐ

Self-Assignment๋ž€?

Cat kitty{"kitty", 1};
kitty = kitty;              // ์ž๊ธฐ ์ž์‹ ์„ ๋Œ€์ž…
kitty = std::move(kitty);   // ์ž๊ธฐ ์ž์‹ ์„ ์ด๋™

๋ฌธ์ œ ์ƒํ™ฉ

  • ๊ธฐ๋ณธ ํƒ€์ž…(int, string ๋“ฑ) ๋ฉค๋ฒ„๋งŒ ์žˆ์œผ๋ฉด ํฐ ๋ฌธ์ œ ์—†์Œ
  • ํฌ์ธํ„ฐ ๋ฉค๋ฒ„ ๋ณ€์ˆ˜๊ฐ€ ์žˆ์œผ๋ฉด ์‹ฌ๊ฐํ•œ ๋ฌธ์ œ ๋ฐœ์ƒ ๊ฐ€๋Šฅ
class Cat
{
    std::string* mName;  // ํฌ์ธํ„ฐ ๋ฉค๋ฒ„
    
public:
    Cat& operator=(const Cat& other)
    {
        delete mName;              // 1. ์ž์‹ ์˜ ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ
        mName = new std::string(*other.mName);  // 2. other๋„ ๊ฐ™์€ ๊ฐ์ฒด๋ฉด?
        // โ†’ ์ด๋ฏธ ํ•ด์ œ๋œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ฝ์œผ๋ ค๊ณ  ์‹œ๋„! (Undefined Behavior)
        return *this;
    }
};

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•: Self-Assignment ์ฒดํฌ

Cat& operator=(const Cat& other)
{
    if (&other == this) return *this;  // โœ… ์ž๊ธฐ ์ž์‹ ์ด๋ฉด ๋ฐ”๋กœ ๋ฆฌํ„ด
    
    mName = other.mName;
    mAge = other.mAge;
    std::cout << mName << " copy assignment" << std::endl;
    return *this;
}

Cat& operator=(Cat&& other) noexcept
{
    if (&other == this) return *this;  // โœ… ์ด๋™์—์„œ๋„ ์ฒดํฌ ํ•„์š”
    
    mName = std::move(other.mName);
    mAge = other.mAge;
    std::cout << mName << " move assignment" << std::endl;
    return *this;
}

๐Ÿ”ธ noexcept ํ‚ค์›Œ๋“œ

noexcept๋ฅผ ๋ถ™์—ฌ์•ผ ํ•˜๋Š” ํ•จ์ˆ˜
1. Destructor (์†Œ๋ฉธ์ž)
2. Move Constructor (์ด๋™ ์ƒ์„ฑ์ž)
3. Move Assignment (์ด๋™ ๋Œ€์ž… ์—ฐ์‚ฐ์ž)

์ด์œ :

  • ์œ„ ์„ธ ํ•จ์ˆ˜๋Š” ์ƒˆ๋กœ์šด ๋ฆฌ์†Œ์Šค๋ฅผ ์š”์ฒญํ•˜์ง€ ์•Š์Œ
  • ๋”ฐ๋ผ์„œ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น ์‹คํŒจ ๋“ฑ์˜ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์—†์Œ
  • noexcept๋ฅผ ๋ช…์‹œํ•˜๋ฉด ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋” ํšจ์œจ์ ์ธ ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Œ
  • STL ์ปจํ…Œ์ด๋„ˆ๊ฐ€ move semantics๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ
~Cat() noexcept {}  // ์†Œ๋ฉธ์ž๋Š” ์•”๋ฌต์ ์œผ๋กœ noexcept์ด์ง€๋งŒ ๋ช…์‹œ ๊ฐ€๋Šฅ

Cat(Cat&& other) noexcept 
    : mName{std::move(other.mName)}
    , mAge{other.mAge} {}

Cat& operator=(Cat&& other) noexcept
{
    if (&other == this) return *this;
    mName = std::move(other.mName);
    mAge = other.mAge;
    return *this;
}

noexcept์˜ ์ค‘์š”์„ฑ

std::vector<Cat> cats;
cats.push_back(Cat{"kitty", 1});

// vector๊ฐ€ ํฌ๊ธฐ๋ฅผ ๋Š˜๋ฆด ๋•Œ:
// - Move constructor๊ฐ€ noexcept๋ฉด โ†’ ์ด๋™ ์‚ฌ์šฉ (๋น ๋ฆ„)
// - noexcept๊ฐ€ ์—†์œผ๋ฉด โ†’ ๋ณต์‚ฌ ์‚ฌ์šฉ (๋А๋ฆผ, ์•ˆ์ „ํ•จ)

๐Ÿ”ธ ํŠน์ˆ˜ ๋ฉค๋ฒ„ ํ•จ์ˆ˜ ์ œ์–ด

ํ•จ์ˆ˜ ์‚ฌ์šฉ ๊ธˆ์ง€: = delete

class Cat
{
public:
    Cat(const Cat& other) = delete;  // ๋ณต์‚ฌ ์ƒ์„ฑ์ž ์‚ฌ์šฉ ๊ธˆ์ง€
    Cat& operator=(const Cat& other) = delete;  // ๋ณต์‚ฌ ๋Œ€์ž… ์‚ฌ์šฉ ๊ธˆ์ง€
};

int main()
{
    Cat kitty{"kitty", 1};
    Cat kitty2{kitty};  // โŒ ์ปดํŒŒ์ผ ์—๋Ÿฌ!
    return 0;
}

์‚ฌ์šฉ ์‚ฌ๋ก€:

  • ๋ณต์‚ฌํ•˜๋ฉด ์•ˆ ๋˜๋Š” ๋ฆฌ์†Œ์Šค ๊ด€๋ฆฌ ํด๋ž˜์Šค (ํŒŒ์ผ ํ•ธ๋“ค, ๋ฎคํ…์Šค ๋“ฑ)
  • ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด ๊ตฌํ˜„
  • ์ด๋™๋งŒ ํ—ˆ์šฉํ•˜๊ณ  ๋ณต์‚ฌ๋Š” ๊ธˆ์ง€ํ•˜๋ ค๋Š” ๊ฒฝ์šฐ

C++11 ์ด์ „ ๋ฐฉ์‹

class Cat
{
private:
    Cat(const Cat& other);  // ์„ ์–ธ๋งŒ ํ•˜๊ณ  ๊ตฌํ˜„ํ•˜์ง€ ์•Š์Œ
    Cat& operator=(const Cat& other);
    // private์ด๋ฏ€๋กœ ์™ธ๋ถ€์—์„œ ํ˜ธ์ถœ ๋ถˆ๊ฐ€
    // ๊ตฌํ˜„์ด ์—†์œผ๋ฏ€๋กœ ํด๋ž˜์Šค ๋‚ด๋ถ€์—์„œ๋„ ๋งํฌ ์—๋Ÿฌ ๋ฐœ์ƒ
};

Rule of Zero/Three/Five

  • Rule of Zero: ํŠน์ˆ˜ ๋ฉค๋ฒ„ ํ•จ์ˆ˜๋ฅผ ํ•˜๋‚˜๋„ ์ •์˜ํ•˜์ง€ ์•Š์Œ (์ปดํŒŒ์ผ๋Ÿฌ์—๊ฒŒ ๋งก๊น€)
  • Rule of Three: ์†Œ๋ฉธ์ž, ๋ณต์‚ฌ ์ƒ์„ฑ์ž, ๋ณต์‚ฌ ๋Œ€์ž… ์ค‘ ํ•˜๋‚˜๋ฅผ ์ •์˜ํ•˜๋ฉด ๋‚˜๋จธ์ง€๋„ ์ •์˜
  • Rule of Five: ์œ„ 3๊ฐœ + ์ด๋™ ์ƒ์„ฑ์ž + ์ด๋™ ๋Œ€์ž… (C++11 ์ดํ›„)

<์ฐธ๊ณ  ์ž๋ฃŒ>

์ฝ”๋“œ์—†๋Š” ํ”„๋กœ๊ทธ๋ž˜๋ฐ
C++ Constructors and Destructors

0๊ฐœ์˜ ๋Œ“๊ธ€