C++/문법/클래스

덤프버전 :

파일:나무위키+넘겨주기.png   관련 문서: C++

파일:나무위키+상위문서.png   상위 문서: C++/문법



1. 개요
2. 캡슐화
2.1. 정보 은닉
2.2. 데이터 멤버
2.3. 멤버 함수
2.4. this
2.5. 정적 멤버
2.6. 멤버 자료형
2.6.1. 멤버 클래스
2.6.2. friend
2.6.3. 자료형 별칭
3.1. 다중 상속
4. 다형성
4.1. 가상 멤버 함수
4.2. 추상 멤버 함수
5.1. 초기자(Initializer)
5.2. explicit
6. 소멸자
7. 특수 생성자
7.1. 복사 생성자
7.2. 복사 대입 연산자
7.3. 이동 생성자
7.4. 이동 대입 연산자
7.5. default
8. 자명성
8.1. 리터럴



1. 개요[편집]


class /*클래스 이름*/
{
    ...
};

C++클래스객체 지향 프로그래밍에 관해 포괄적으로 설명하는 문서. 객체 지향 언어들은 보통 클래스라고 부르는 독립되고 주어진 역할을 수행할 수 있는 객체를 가지고 있다. 마찬가지로 C++에서도 클래스를 사용할 수 있다. 본래 C++의 클래스는 C의 구조체로부터 내려왔지만 지금은 여러가지 독특한 요소를 많이 가지고 있다. C의 구조체는 그저 순수한 메모리 규격이다. 마치 JSON처럼 이름 붙인 자료형만을 나열한 꼴이었다. 그리고 구조체의 상호작용은 반드시 구조체 인스턴스와 별도의 함수가 필요했다.

C++의 클래스는 생성자로 인스턴스가 생성되는 방식을 결정할 수 있고, 함수 인터페이스를 클래스 안으로 들여 간편한 상호작용이 가능해졌다. 그러나 이것만으로는 클래스가 정말 다른 클래스와 상호작용하고 있다고 말하기는 힘들다. 다른 클래스로부터 속성을 받아오는 상속과 다형성은 이를 발전시켰다. 연산자 오버로딩을 통해 비록 생소하지만, 어디에서나 일관된 코드 작성을 할 수 있다. 그리고 정적 속성과 템플릿은 클래스가 어떤 특징을 가지고 있는지 메타 데이터로 표현할 수 있도록 해줬으며, 클래스의 인스턴스를 만들 필요도 없게 해주었다. 마지막으로 소멸자를 통해 객체가 파괴되는 방식도 결정할 수 있다. C++의 가장 큰 특징인 저수준의 메모리 제어도 가능한 점을 종합해보면, C++의 클래스는 단순히 자료를 읽고 쓰게 해주는 인터페이스가 아니라, 정보가 어떻게 흘러가는지 정의한 규칙 모음이라고 말할 수 있다. 다른 언어의 정말 주체적인 클래스와는 조금 다르지만, 지향하는 바는 같다.


2. 캡슐화[편집]


class Squirrel
{
public:
    std::string myName;
    size_t myAge;
    float myWeight;

    std::vector<Acorn*> myAcorns;
};
속성 (Property)
클래스는 '속성''이라고 칭하는 여러가지 정보를 담을 수 있다. 속성의 종류에는 데이터 멤버, 멤버 함수, 자료형 별칭 3가지가 존재한다. 여기서 정적인 멤버는 따로 설명한다.


2.1. 정보 은닉[편집]


struct Nest
{
    // public
    std::vector<Acorn*> everyAcorns;
};

class Squirrel
{
public:
    std::string myName;
    size_t myAge;
    float myWeight;

private:
    Nest* myNest;
};
접근성 지시자(Access specifiers)
private
,
protected
,
public
은 속성에 대한 접근 권한을 지정하는 지시자이다.
private
는 해당 클래스의 멤버 함수만 접근할 수 있음을 뜻하고,
public
은 해당 클래스의 외부에서 접근할 수 있다는 것을 뜻한다. 기본적으로 클래스는
private
권한이 적용되어 있다. 반면 C의 구조체는
public
이 적용된다. 한편
protected
는 자신의 멤버 함수 및 해당 클래스를 상속받는 자식 클래스의 멤버 함수만 접근할 수 있음을 뜻한다. 이에 대한 건 하단의 상속 문단에서 설명한다.


2.2. 데이터 멤버[편집]


class Squirrel
{
public:
    std::string myName;
    size_t myAge;
    float myWeight;

private:
    Nest* myNest;
};

int main()
{
    Squirrel squirrel1;
    squirrel1.myName = "The first squirrel";
    squirrel1.myAge = 1;
    squirrel1.myWeight = 2.0f;
    squirrel1.myNest = new Nest; // 접근 권한 오류!

    Squirrel squirrel2{}; // 중괄호를 사용한 기본 초기화
    size_t& squirrel2_age = squirrel2.myAge; // 이제 squirrel2_age로 접근 가능함

    Squirrel squirrel3{ "The third squirrel", 3, 1.6f }; // 결집 구조체(Aggregate) 방식의 초기화
}
소속 정보(Data Member)
소속 변수(Member Variable) 내지는 필드(Field)라고도 부르는 데이터 멤버는 클래스가 가진 대표적인 속성이다. 클래스의 인스턴스를 생성하면
.
연산자, 포인터의 경우
->
연산자로 접근할 수 있다. 또, 인스턴스의 필드를 참조형으로 가져오면 참조 변수로도 접근할 수 있다.
enum class Gender { NonBinary = 0, Male, Female, Lesbian, Gay, Binary, TransgenderMF, TransgenderFM, };

class Squirrel
{
public:
    std::string myName;
    size_t myAge;
    float myWeight;
    Gender myGender;

    const bool canFly; // 상수 변수는 반드시 초기화 해야 한다
    Nest& myNest; // 'myNest' 참조 변수는 반드시 초기화해야 한다
};

int main()
{
    Squirrel squirrel1{}; // 오류! 상수와 참조 멤버를 초기화하지 않았습니다.

    Nest nest_in_tree{}; // 도토리 0개
    Squirrel squirrel2{ "The second squirrel", 5, 3.0f, Gender::Male, false, nest_in_tree };

    Acorn* nut_on_floor = new Acorn{};
    squirrel2.myNest.push_back(nut_on_floor); // .로 벡터 필드 'myNest' 접근 (nest_in_tree가 참조됨)

    size_t acorn_count = nest_in_tree.size(); // 도토리 1개
}
클래스의 필드에도 온갖 한정자가 다 붙을 수 있다. 모든 한정자
const
,
volatile
,
&
,
&&
를 사용할 수 있다. 여기서
&
&&
의 경우 인스턴스가 생성될 때 참조할 원본 변수를 반드시 인자로 전달해야 한다. 참고로
&&
은 생성이 끝나면 그 뒤로는 아무런 한정자를 붙이지 않은 것처럼 인스턴스 내부 변수가 된다.

2.3. 멤버 함수[편집]


class Squirrel
{
public:
    void Wake() {}
    void Sleep() {}
    void Run() {}
    void Eat(Acorn* acorn) {}
    void Save(Acorn* acorn) {}
    
    std::string GetName() const { return myName; }
    size_t GetAge() const { return myAge; }
    float GetWeight() const { return myName; }

    std::string myName;
    size_t myAge;
    float myWeight;

private:
    std::vector<Acorn*> myAcorns;
};
소속 함수(Member Function)
메서드(수단, Method)라고도 부르는 멤버 함수는 클래스의 행동, 동작을 정의하는 함수들이다.


2.4. this[편집]


import <print>;

class MyClass {
public:
    void GetMember()
    {
        int variable = 7331;
        this->variable = 1337;
        std::println(variable, this->variable); // 7331, 1337
    }

    void GetMember2()
    {
        variable = 1337; // 필드
        std::println(this->variable); // 1337
    }

private:
    int variable;
};
this

인스턴스 자기 자신의 주소를 가진 내장 포인터 변수다. 객체 지향 언어에서는 메서드를 구현할 때에 매개변수의 자료형과 이름을 필드의 경우와 동일하게 지정해도 혼선이 생기지 않는다. 다만, 필드와 메서드 내의 지역 변수를 구분하기 위해서는 활용할 수 있다.


2.5. 정적 멤버[편집]


class Squid
{
public:
    static Squid* Create()
    {
        return new Squid;
    }

    static constexpr bool isMammal = false;
    static const bool canFly; 
};
const bool Squid::canFly = false; // C 방식의 정적 필드 정의

class Squirrel
{
public:
    static Squirrel* Create()
    {
        return new Squirrel;
    }

    static constexpr bool isMammal = true;
    static inline const bool canFly = false; // C++11의 inline을 사용한 정적 필드 정의
};

class FlyingSquirrel
{
public:
    static FlyingSquirrel* Create()
    {
        return new FlyingSquirrel;
    }

    static constexpr bool isMammal = true;
    constinit static bool canFly = true; // C++17의 constinit을 사용한 컴파일 시간 정적 필드 정의
};
Static Member


2.6. 멤버 자료형[편집]



2.6.1. 멤버 클래스[편집]


import <string>;
import <algorithm>;

struct Person;

class Airplane
{
public:
    [[nodiscard]]
    constexpr bool IsOk() const noexcept
    {
        return airplaneCore.IsOk();
    }

protected:
    struct Machine // Airplane::Machine
    {
        [[nodiscard]]
        static constexpr bool CheckOk(const Machine& machine) noexcept // Airplane::Machine::CheckOk
        {
            return isOk;
        }

        bool isOk = false;
    };

    struct Core // Airplane::Core
    {
        struct Body // Airplane::Core::Body
        {
            struct Client // Airplane::Core::Body::Client
            {
                [[nodiscard]]
                constexpr bool IsOk() const noexcept
                {
                    return std::all_of(std::begin(myChairs), std::end(myChairs), Machine::CheckOk)
                        && std::all_of(std::begin(myAirpockets), std::end(myAirpockets), Machine::CheckOk);
                }

                Machine myChairs[250];
                Person myMembers[250];
                Machine myAirpockets[2];
            }

            [[nodiscard]]
            constexpr bool IsOk() const noexcept
            {
                return std::all_of(std::begin(myWings), std::end(myWings), Machine::CheckOk)
                    && std::all_of(std::begin(myTailWings), std::end(myTailWings), Machine::CheckOk)
                    && std::all_of(std::begin(myAntennas), std::end(myAntennas), Machine::CheckOk)
                    && myClients.IsOk();
            }

            Machine myWings[2];
            Machine myTailWings[3];
            Machine myAntennas[6];
            Client myClients;
        };

        [[nodiscard]]
        constexpr bool IsOk() const noexcept
        {
            return std::all_of(std::begin(myEngines), std::end(myEngines), Machine::CheckOk)
                && myDisplay.IsOk() && myFuelTank.IsOk()
                && myBody.IsOk();
        }

        Machine myDisplay;
        Machine myFuelTank;
        Machine myEngines[4];
        Body myBody;
    };

    Core airplaneCore;

private:
    std::string myName;
    Person myEmployee[20];
    Person myPilots[2];
}
멤버 클래스(Member Class)


2.6.2. friend[편집]


친구 클래스(Friend Class)


2.6.3. 자료형 별칭[편집]


import <memory>;

template<typename T>
class Trait
{
public:
    using type = T;
    using value_type = T;
    using const_type = const T;
    using reference = T&;
    using const_reference = const T&;
    using rvalue_reference = T&&;
    using const_rvalue_reference = const T&&;
    using pointer = T*;
    using const_pointer = const T*;
    using difference_type = std::ptrdiff_t;
};

template<typename _Ty, typename _Trait = Trait<_Ty>>
class MyVector
{
public:
    using value_type = _Trait<_Ty>::value_type;
    using const_type = _Trait<_Ty>::const_type;
    using reference = _Trait<_Ty>::reference;
    using const_reference = _Trait<_Ty>::const_reference;
    using rvalue_reference = _Trait<_Ty>::rvalue_reference;
    using const_rvalue_reference = _Trait<_Ty>::const_rvalue_reference;
    using pointer = _Trait<_Ty>::pointer;
    using const_pointer = _Trait<_Ty>::const_pointer;
    using difference_type = _Trait<_Ty>::difference_type;

    constexpr void Push(const_reference element);
    constexpr void Push(rvalue_reference element);

private:
    value_type* myBuffer;
};
자료형 별칭(Type Alias)



3. 상속[편집]


import <iostream>;
import <string>;

class MyBase {
public:
    void print()
    {
        std::cout << text << 64 << '\n'; // My number is 64
    }

protected:
    std::string text = "My number is ";
};

class MyDerived : public MyBase {
public:
    void print()
    {
        std::cout << text << a << '\n'; // My number is 128
    }

    void legacy_of_void()
    {
        MyBase::print(); // 정적 메서드를 호출하는 것이 아니라, 기반 클래스의 print를 호출한다
    }

private:
    int a = 128;
};
상속(Inheritance)
어떤 클래스는 다른 클래스와 종속관계를 형성하고 속성을 가져올 수 있다. 이것을 상속이라고 한다. 상속은 객체 지향언어에서 가장 핵심이 되는 요소다. 상속으로 클래스를 재사용하고 작성해야할 코드의 양을 줄일 수 있다. 같은 동작을 하나의 인터페이스로 통일할 수 있다. 클래스의 핵심을 제외하고 사용에 불필요한 요소를 숨길 수 있다. 여기서 상속의 대상이 되는 클래스를 기반 클래스(Base Class), 상속을 받는 클래스를 파생 클래스(Derived Class)라고 칭한다. 더 친근한 용어로는 부모 클래스(Parent Class)자식 클래스(Child Class)라고 부른다.

C++에서는
class [파생 클래스 이름] : [접근성 지시자] [기반 클래스 이름]
의 형식으로 상속을 수행할 수 있다. 기반 클래스에서 속성을 가져오는 방법을 접근성 지시자를 통해 결정할 수 있다. 멤버에 대한 접근 권한과 똑같이
private
,
protected
,
public
이 있으나 둘을 혼동하면 안된다. 상속 방식에 따라서 파생 클래스에서 접근하는 부모 클래스의 멤버에 대한 접근 권한이 달라진다. 파생 클래스와 기반 클래스의 속성은 이름이 달라도, 같아도 상관 없다. 상속이 수행되면 파생 클래스에서는 기반 클래스의
public
,
protected
멤버를 사용할 수 있다.
protected
는 자신의 멤버 함수 및 해당 클래스를 상속받는 자식 클래스의 멤버 함수만 접근할 수 있음을 뜻한다.기반 클래스의 멤버를 호출하고 싶다면 그냥 하면 된다. 그러나 이름이 겹치는 멤버는 구분해줘야 한다.
기반 클래스 이름::속성
과 같이 사용해야 한다.

파생 클래스 멤버
private 상속
protected 상속
public 상속
private 멤버
접근 불가
접근 불가
접근 불가
protected 멤버
접근 불가
자신만 접근[protected]
자신만 접근[protected]
public 멤버
접근 불가
자신만 접근[protected]
공개적 접근[public]

예제에서
MyDerived
는 부모 클래스인
MyBase
로부터
public
방식으로 상속받았다. 자식 클래스는 부모 클래스인
MyBase
로부터
std::string
자료형인 변수
text
와 메서드
print()
를 물려받는다.
MyDerived
에서 별도로 선언하지 않았으나
MyBase
클래스에서 필드
text
protected
권한으로 선언하고,
public
상속을 했기 때문에 접근이 가능하다.
MyDerived
는 자신만이 갖는 필드
a
와 메서드
legacy_print()
도 갖고 있다.


3.1. 다중 상속[편집]


class BruceWayne : public RichMan, private Batman
{
    ...
};

C++C\#, 자바와는 달리 여러 개의 클래스로부터 상속이 가능하다. 부모 클래스의 개수에는 제한이 없다.이 예제에서 클래스
BruceWayne
RichMan
라는 클래스로부터
public
상속을 받고,
Batman
라는 클래스에서는
private
상속을 받는다.

자바와는 달리 C++의 경우에는 다중 상속이 가능하다는 특성으로 인해 한단계 상위 클래스를 호출하는 키워드가 존재할 수가 없다. 그래서 자식 클래스를 구현할 때에 자식 클래스 자기 자신의 멤버인지 아니면 어떤 부모 클래스의 멤버를 사용할지를 정확하게 표현해야할 필요가 있다.


4. 다형성[편집]


하위 유형 다형성(Subtype Polymorphism)


4.1. 가상 멤버 함수[편집]







}virtual ~MyParent() = default;};class MyChild : public MyParent{private:std::string str = u8"안녕하세요.";public:void print() overridestd::cout << str << '\n';{
}};
">
부모 클래스인
MyParent
의 멤버 함수인
print()
함수에서는 멤버 변수인
text
의 내용인
Hello World
를 출력한다. 그러나 자식 클래스인
My Child
에서는 멤버 변수인
str
의 내용인 안녕하세요.를 출력하도록
print()
함수를 재정의하였다.
자식 클래스에서 부모 클래스의 멤버 함수를 재정의하려면 부모 클래스에는
virtual [반환형] [함수명] (매개 변수)
라고 자식에게 상속할 멤버 함수의 원형을 정의한다. 자식 클래스에서는
[반환형] [부모 클래스의 멤버 함수명] ([해당 함수의 멤버 변수]) override
라고 원형을 정의한다. 이때 부모 클래스의 함수를 가상 함수라고 한다. C++에서는 가상 함수만 동적 바인딩을 지원하며 상속이 가능하다.


4.2. 추상 멤버 함수[편집]



5. 생성자[편집]



}MyClass(매개 변수...)...{
}};
">
생성자는 클래스 이름([매개 변수]) 형식으로 정의되는 특수 함수다 [1]. 인스턴스의 생성 시점에서 초기화를 위하여 실행되는 함수이다. 일반 함수와 마찬가지로 생성자에도
constexpr
,
consteval
,
noexcept
를 사용할 수 있다. 이중에 매개변수가 없는 생성자를 기본 생성자 (Default Constructor)라고 부른다.

5.1. 초기자(Initializer)[편집]


class MyABCClass {
public:
    MyABCClass(int a, int b, int c)
        : A(a), B(b), C(c)
    {}

private:
    int A, B, C; // 필드의 선언은 뒤쪽에 위치시킬 수 있다.
};

이 예제에서 생성자의 닫는 괄호 뒤에 오는
: A(a), B(b), C(C)
이니셜라이저이다. 클래스 인스턴스의 생성 순간에 명시한 필드를 초기화하는 기능을 수행한다. 이 예제에서는
A = a
,
B = b
,
C = c
생성이 실행되었다.

한편, 이니셜라이저는 멤버 변수로써 클래스 인스턴스의 초기화도 가능하다.
class MyNestedClass {
public:
    MyNestedClass(int a, int b, int c)
        : abc(a, b, c)
    { ... }

private:
    MyABCClass abc;
};

이 예제에서는
: abc(a, b, c)
가 이니셜라이저이다.
abc
라는
MyABCClass
클래스의 인스턴스가 이니셜라이저를 통해
abc(int a, int b, int c)
라는 생성자를 자동적으로 호출하여 초기화된다. 정리하자면 이니셜라이저는 함수의 원형 뒤에 바로 정의하며 : [초기화 할 멤버 변수 1](매개 변수), [초기화 할 멤버 변수 2](매개 변수), ...라고 적으면 된다.

이니셜라이저를 잘 활용하면 객체 생성의 번거로움을 줄일 수 있다.

5.2. explicit[편집]


명시적 (Explicit)



6. 소멸자[편집]


소멸자는 ~클래스 이름()형식으로 정의되는 특수 함수다. 클래스 인스턴스의 파괴 시점에서 필드의 메모리 반환을 위하여 실행되는 함수이다.
class MyClass {
private:
    const char* myStr;

public:
    MyClass()
        : myStr(new const char[100])
    {}

    ~MyClass()
    {
        delete[] myStr;
    }
};
위 예제에서
~MyClass()
가 소멸자이다. 생성자에서 동적 할당된
char
자료형인
myStr
를 소멸자
~MyClass()
에서
delete[]
를 통해 메모리를 수동으로 해제하고 있다.

만일 클래스 내부에서 동적 할당 등을 통해 관리하는 리소스가 있다면 소멸자 내부에 이 동적할당을 받은 리소스들에 대한 해제 코드를 작성 해줘야 한다. 만일 예외가 발생하면 일반적으로는 해당 위치에서 함수를 종료하고 문맥을 반환하게 되는데, 만일 메모리 해제해 주는 코드가 이 뒤에 있다면 해당 코드가 실행되지 않아 얄짤없이 메모리 누수가 일어나게 된다. 하지만 소멸자는 예외로 인해 종료되는 상황에서도 반드시 호출되기 때문에 꼭 소멸자 내부에 동적으로 할당받은 리소스들에 대한 해제 코드를 작성해주도록 하자.

또한 상속 관계에 있는 클래스의 경우 기본 클래스의 소멸자를 가상 함수로 지정하는 것이 좋다. 예를 들어 소멸자가 가상 함수가 아닌 클래스 A와 A를 상속하는 클래스 B가 있을 때, 기본 클래스인 A를 가리키는 포인터로 B의 객체를 호출한다면 클래스 B의 소멸자가 아닌 클래스 A의 소멸자를 호출하기 때문에 메모리 누수가 발생한다. 기본 클래스의 소멸자를
virtual
로 지정하면 소멸자를 동적 바인딩하므로 이런 문제를 방지할 수 있다.
class MyClass17 {
public:
    constexpr MyClass17(std::string_view name) noexcept
        : myName{ name.begin(), name.end() } // C++17에서는 std::string_view를 std::string에 직접 대입할 수가 없었다.
        : myName(name) // C++23부터 std::string_view를 std::string에 대입할 수 있다.
    {}

    ~MyClass17() noexcept
    {}

private:
    std::string myName;
};
C++17부터는 소멸자에
noexcept
를 사용할 수 있다.
template<size_t Capacity>
class MyParent20 {
public:
    static constexpr size_t myCapacity = Capacity;

    constexpr MyParent20() noexcept
        : myBuffer(new char[Capacity]), myCapacity(Capacity)
    {}

    virtual constexpr ~MyParent20() noexcept
    {
        delete[] myBuffer;
    }

    virtual constexpr size_t GetCapacity() const noexcept
    {
        return myCapacity;
    }

protected:
    char* myBuffer;
};

template<size_t Capacity, size_t InnerCapacity>
class MyClass20 : public MyParent20<InnerCapacity> {
public:
    static constexpr size_t myCapacity = Capacity;

    constexpr MyClass20() noexcept // 부모의 생성자는 명시해줘야 한다.
        : MyParent20()
        , childBuffer(new char[Capacity])
    {}

    // 파괴될 때 MyParent20::~MyParent20()이 실행된다.
    constexpr ~MyClass20() noexcept(std::is_nothrow_destructible_v<std::string>) // noexcept(true)
    {}

    constexpr size_t GetCapacity() const noexcept override
    {
        return myCapacity;
    }
};

private:
    std::string childBuffer;
};
C++20부터는
constexpr
역시 사용할 수 있다. 그리고
virtual constexpr
이 가능해졌다.


7. 특수 생성자[편집]



7.1. 복사 생성자 [편집]


class MyClass
{
public:
    MyClass(const MyClass& other); // (1) 참조하는 복사 생성자

    MyClass(MyClass other); // (2) 구식 값 생성자

    int value;
};

int main()
{
    MyClass my_instance_0{};
    my_instance_0.value = 3000;

    MyClass my_instance_1{ my_instance_0 }; // 복사 생성자 수행
    // my_instance_1.value는 3000

    return 0;
}
지금까지 우리가 클래스를 만들 때는 모두
Class{
},
Class(params...)
,
new Class{}
따위를 수행 해주었다. 이 방식은 직관적이지만 문제가 있는데, 예를 들자면 인스턴스 생성에 필요한 인자들을 전달하는 작업이 반복적이다. 그리고 속성이 조금씩 다른 인스턴스들을 만들 때는 불편해진다. 이를 위해 C++에서는 기본적으로 복사 생성자라고 하는, 생성자의 특수한 형태를 지원한다. 사용법은 간단하게 클래스 생성자의 인자로 먼저 만들어진 인스턴스 변수를 전달하는 것이다.



7.2. 복사 대입 연산자[편집]


MyClass& operator=(const MyClass& other);
복사 생성자가 기본적으로 존재하면 복사 대입 연산자도 존재한다. 그런데 복사가 불가능한 클래스에 새로 복사 생성자를 만들어도 복사 대입 연산자가 자동으로 정의되지는 않는다. 표준 라이브러리의 `std::is_copy_constructible`, `std::is_copy_assignable`의 구분은 이를 위한 것이다. 그러나 제약조건 `std::copyable`은 둘 모두를 검사하므로 이를 사용하기 위해서는 이동 생성자, 이동 대입 연산자를 둘 다 제대로 구현해줘야 한다.


7.3. 이동 생성자[편집]


class MyClass
{
public:
    MyClass(MyClass&& other); // C++11에서 도입된 이동 생성자

    MyClass(MyClass other); // 값 생성자는 현재는 이동 연산도 받을 수 있다. 그러나 other까지만 이동이 실행된다.
};



7.4. 이동 대입 연산자[편집]


MyClass& operator=(MyClass&& other);
이동 생성자가 기본적으로 존재하면 이동 대입 연산자도 존재한다. 그런데 이동이 불가능한 클래스에 새로 이동 생성자를 만들어도 이동 대입 연산자가 자동으로 정의되지는 않는다. 표준 라이브러리의 `std::is_move_constructible`, `std::is_move_assignable`의 구분은 이를 위한 것이다. 그러나 제약조건 `std::movable`은 둘 모두를 검사하므로 이를 사용하기 위해서는 이동 생성자, 이동 대입 연산자를 둘 다 제대로 구현해줘야 한다.


7.5. default[편집]


class MyClass
{
public:
    MyClass() = default;
    ~MyClass() = default;
    
    MyClass(const MyClass&) = default;
    MyClass(MyClass&&) = default;

    MyClass& operator=(const MyClass&) = default;
    MyClass& operator=(MyClass&&) = default;

    bool operator==(const MyClass&) const = default;
    auto operator<=>(const MyClass&) const = default;
}



8. 자명성[편집]


자명한 클래스 (Trivial)
자명한 클래스는 C++ 코드 어디에서나, 자연스럽게 존재하는, 스스로 존재하는 클래스라는 뜻이다. 자명한 클래스는 다른 클래스에 의존하지 않고, 참조하지도 않는다. 또한 어떠한 문맥에도 의존하지 않는다. 클래스 자명성의 의의는 이 클래스의 존재, 생성, 파괴가 컴파일 시점에 결정될 수 있다는 것이다. 즉, 어떠한 부작용(Side Effect) 없이, 결정론적인(Deterministic) 코드를 생성할 수 있다. 결정론적 코드 안에서 클래스는 데이터의 흐름을 결정하고 사용자는 그 결과를 `O(1)`의 시간에 확인할 수 있다. 곧,
constexpr
함수의 반환형, 인자로써, 그리고 함수 내부에서 생성하고 사용할 수 있다.

자명한 클래스가 되기 위한 필요조건은 다음과 같다:


8.1. 리터럴[편집]


리터럴 클래스 (Literal)
자명한 클래스이면서 기본 생성자와 소멸자가
constexpr
,
noexcept
인 클래스. 템플릿 내의 매개변수로 사용할 수 있다. 당장 모든 원시자료형이 리터럴이다. C++20에 와서는
std::string
도 리터럴 클래스가 되었다.

[protected] A B C [public] [1] 함수 명칭을 클래스 이름과 같게 두고, 반환형을 명시하지 않는다

파일:CC-white.svg 이 문서의 내용 중 전체 또는 일부는
문서의 r263 판{{{#!wiki style="display: inline; display: 5;"
, 5번 문단}}}에서 가져왔습니다. 이전 역사 보러 가기
파일:CC-white.svg 이 문서의 내용 중 전체 또는 일부는 다른 문서에서 가져왔습니다.
[ 펼치기 · 접기 ]
문서의 r263 판{{{#!wiki style="display: inline; display: 5;"
, 5번 문단}}} (이전 역사)
문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)




파일:크리에이티브 커먼즈 라이선스__CC.png 이 문서의 내용 중 전체 또는 일부는 2023-10-21 19:49:16에 나무위키 C++/문법/클래스 문서에서 가져왔습니다.