수색…


가상 및 보호 된 소멸자

상속받을 수 있도록 고안된 클래스를 Base 클래스라고합니다. 그러한 수업의 특별 회원 기능에주의를 기울여야한다.

실행시에 다형 적으로 (기본 클래스에 대한 포인터를 통해) 사용되도록 설계된 클래스는 소멸자 virtual 선언해야합니다. 이렇게하면 객체가 기본 클래스에 대한 포인터를 통해 파기되는 경우에도 객체의 파생 된 부분이 제대로 파괴 될 수 있습니다.

class Base {
public:
    virtual ~Base() = default;

private:
    //    data members etc.
};

class Derived : public Base { //  models Is-A relationship
public:
    //    some methods

private:
    //    more data members
};

//    virtual destructor in Base ensures that derived destructors
//    are also called when the object is destroyed
std::unique_ptr<Base> base = std::make_unique<Derived>();
base = nullptr;  //    safe, doesn't leak Derived's members

클래스가 다형성 일 필요는 없지만 인터페이스가 상속 될 수 있도록해야하는 경우 가상이 아닌 protected 소멸자를 사용하십시오.

class NonPolymorphicBase {
public:
    //    some methods

protected:
    ~NonPolymorphicBase() = default; //    note: non-virtual

private:
    //    etc.
};

이러한 클래스는 포인터를 통해 절대로 파기 될 수 없으므로 슬라이싱으로 인한 자동 누출을 피할 수 있습니다.

이 기술은 특히 private 기본 클래스로 설계된 클래스에 적용됩니다. 이러한 클래스는 몇 가지 일반적인 구현 세부 사항을 캡슐화하는 데 사용되며 virtual 메소드를 사용자 정의 포인트로 제공합니다. 이러한 종류의 클래스는 다형 적으로 사용되어서는 안되며 protected 소멸자는이 요구 사항을 코드에 직접 문서화하는 데 도움이됩니다.

마지막으로, 일부 클래스는 기본 클래스로 사용 되지 않을 것을 요구할 수도 있습니다. 이 경우 클래스를 final 로 표시 할 수 있습니다. 이 경우에는 정상적인 가상이 아닌 public 소멸자가 좋습니다.

class FinalClass final {  //    marked final here
public:
    ~FinalClass() = default;

private:
    //    etc.
};

암시 적 이동 및 복사

소멸자를 선언하면 컴파일러가 암시 적 이동 생성자를 생성하고 할당 연산자를 옮기지 않습니다. 소멸자를 선언하는 경우 이동 작업에 대한 적절한 정의도 추가해야합니다.

또한 이동 작업을 선언하면 복사 작업이 생성되지 않으므로 추가해야합니다 (이 클래스의 개체에 복사 의미가 있어야하는 경우).

class Movable {
public:
    virtual ~Movable() noexcept = default;

    //    compiler won't generate these unless we tell it to
    //    because we declared a destructor
    Movable(Movable&&) noexcept = default;
    Movable& operator=(Movable&&) noexcept = default;

    //    declaring move operations will suppress generation
    //    of copy operations unless we explicitly re-enable them
    Movable(const Movable&) = default;
    Movable& operator=(const Movable&) = default;
};

복사 및 스왑

리소스를 관리하는 클래스를 작성하는 경우 모든 특수 멤버 함수를 구현해야합니다 (3/5 규칙 참조). 복사 생성자와 대입 연산자를 작성하는 가장 직접적인 방법은 다음과 같습니다.

person(const person &other)
    : name(new char[std::strlen(other.name) + 1])
    , age(other.age)
{
    std::strcpy(name, other.name);
}

person& operator=(person const& rhs) {
    if (this != &other) {
        delete [] name;
        name = new char[std::strlen(other.name) + 1];
        std::strcpy(name, other.name);
        age = other.age;
    }

    return *this;
}

그러나이 방법에는 몇 가지 문제점이 있습니다. 그것은 강력한 예외 보증을 실패 - 경우 new[] 던졌습니다, 우리가 이미 소유 한 자원을 해제 한 this 하고 복구 할 수 없습니다. 우리는 카피 할당에서 많은 복사본 구성 논리를 복제하고 있습니다. 그리고 우리는 복사 작업에 오버 헤드를 추가하는 자체 할당 검사를 기억해야하지만 여전히 중요합니다.

강력한 예외 보장을 만족시키고 코드 중복을 피하기 위해 (후속 이동 지정 연산자로 두 배로), 복사 및 스왑 이디엄을 사용할 수 있습니다.

class person {
    char* name;
    int age;
public:
    /* all the other functions ... */

    friend void swap(person& lhs, person& rhs) {
        using std::swap; // enable ADL

        swap(lhs.name, rhs.name);
        swap(lhs.age, rhs.age);
    }

    person& operator=(person rhs) {
        swap(*this, rhs);
        return *this;
    }
};

왜이게 효과가 있니? 우리가 가질 때 어떤 일이 일어나는지 생각해보십시오.

person p1 = ...;
person p2 = ...;
p1 = p2;

첫째, 우리는 p2 에서 rhs 를 복사합니다 (우리는 여기서 복제 할 필요가 없었습니다). 이 연산이 throw되면 operator= 에서 아무 것도하지 않고 p1 은 그대로 유지됩니다. 다음으로, *thisrhs 사이의 멤버를 rhs . 그러면 rhs 는 범위를 벗어난다. operator= 일 때, 이것은 우리가 복제 할 필요가없는 소멸자를 통해 this 의 원래 자원을 암시 적으로 정리합니다. 자체 할당 작업도 가능합니다. 복사 및 스왑 (할당 및 할당 해제가 필요함)이 덜 효율적이지만, 그렇게할만한 시나리오가 아닌 경우이를 고려하여 일반적인 유스 케이스의 속도를 늦추지는 않습니다.

C ++ 11

위의 공식은 이미 이동 할당을 위해 작동합니다.

p1 = std::move(p2);

여기에서 우리는 p2 에서 rhs 를 옮깁니다. 나머지는 모두 유효합니다. 클래스가 이동 가능하지만 복사가 불가능한 경우 삭제 된 복사 생성자로 인해이 대입 연산자가 잘못 작성되므로 복사 할당을 삭제할 필요가 없습니다.

기본 생성자

기본 생성자 는 호출시 매개 변수가 필요없는 생성자 유형입니다. 이 클래스는 생성되는 유형의 이름을 따서 명명되며 해당 생성자의 멤버 함수입니다 (모든 생성자가 있음).

class C{
    int i;
public:
    // the default constructor definition
    C()
    : i(0){ // member initializer list -- initialize i to 0
        // constructor function body -- can do more complex things here
    }
};

C c1; // calls default constructor of C to create object c1
C c2 = C(); // calls default constructor explicitly
C c3(); // ERROR: this intuitive version is not possible due to "most vexing parse"
C c4{}; // but in C++11 {} CAN be used in a similar way

C c5[2]; // calls default constructor for both array elements
C* c6 = new C[2]; // calls default constructor for both array elements

"매개 변수 없음"요구 사항을 충족시키는 또 다른 방법은 개발자가 모든 매개 변수에 기본값을 제공하는 것입니다.

class D{
    int i;
    int j;
public:
    // also a default constructor (can be called with no parameters)
    D( int i = 0, int j = 42 ) 
    : i(i), j(j){
    }
};


D d; // calls constructor of D with the provided default values for the parameters

어떤 상황에서는 (즉, 개발자가 생성자를 제공하지 않고 다른 불합격 조건이없는 경우) 컴파일러는 암시 적으로 빈 기본 생성자를 제공합니다.

class C{
    std::string s; // note: members need to be default constructible themselves
};

C c1; // will succeed -- C has an implicitly defined default constructor

다른 유형의 생성자를 갖는 것은 앞에서 언급 한 실격 조건 중 하나입니다.

class C{
    int i;
public:
    C( int i ) : i(i){}
};

C c1; // Compile ERROR: C has no (implicitly defined) default constructor
c ++ 11

암시 적 기본 생성자 생성을 방지하기 위해 일반적인 기술은 정의가없는 private 으로 선언하는 것입니다. 누군가가 생성자 (컴파일러에 따라 개인 오류에 대한 액세스 또는 링커 오류가 발생 함)를 사용하려고하면 컴파일 오류가 발생합니다.

기본 생성자 (기능상 암시 적 생성자와 유사 함)가 정의되도록 개발자는 명시 적으로 빈 생성자를 작성할 수 있습니다.

c ++ 11

C ++ 11에서 개발자는 delete 키워드를 사용하여 컴파일러가 기본 생성자를 제공하지 못하게 할 수 있습니다.

class C{
    int i;
public:
    // default constructor is explicitly deleted
    C() = delete;
};

C c1; // Compile ERROR: C has its default constructor deleted

또한 개발자는 컴파일러에서 기본 생성자를 제공하도록 명시 적으로 지정할 수도 있습니다.

class C{
    int i;
public:
    // does have automatically generated default constructor (same as implicit one)
    C() = default;

    C( int i ) : i(i){}
};

C c1; // default constructed
C c2( 1 ); // constructed with the int taking constructor 
c ++ 14

<type_traits> std::is_default_constructible 을 사용하여 유형에 기본 생성자가 있는지 (또는 기본 유형인지) 확인할 수 있습니다.

class C1{ };
class C2{ public: C2(){} };
class C3{ public: C3(int){} };

using std::cout; using std::boolalpha; using std::endl;
using std::is_default_constructible;
cout << boolalpha << is_default_constructible<int>() << endl; // prints true
cout << boolalpha << is_default_constructible<C1>() << endl; // prints true
cout << boolalpha << is_default_constructible<C2>() << endl; // prints true
cout << boolalpha << is_default_constructible<C3>() << endl; // prints false
c ++ 11

C ++ 11에서는 펑터가 아닌 std::is_default_constructible 버전을 사용할 수 있습니다.

cout << boolalpha << is_default_constructible<C1>::value << endl; // prints true

폐물 소각로

소멸자 는 사용자 정의 객체가 파괴 될 때 호출되는 인수가없는 함수입니다. ~ 접두사로 소멸되는 유형의 이름을 따서 명명됩니다.

class C{
    int* is;
    string s;
public:
    C()
    : is( new int[10] ){
    }

    ~C(){  // destructor definition
        delete[] is;
    }
};

class C_child : public C{
    string s_ch;
public:
    C_child(){}
    ~C_child(){} // child destructor
};

void f(){
    C c1; // calls default constructor
    C c2[2]; // calls default constructor for both elements
    C* c3 = new C[2]; // calls default constructor for both array elements

    C_child c_ch;  // when destructed calls destructor of s_ch and of C base (and in turn s)

    delete[] c3; // calls destructors on c3[0] and c3[1]
} // automatic variables are destroyed here -- i.e. c1, c2 and c_ch

대부분의 경우 (즉, 사용자가 소멸자를 제공하지 않고 다른 부적격 조건이없는 경우) 컴파일러는 암시 적으로 기본 소멸자를 제공합니다.

class C{
    int i;
    string s;
};

void f(){
    C* c1 = new C;
    delete c1; // C has a destructor
}

class C{
    int m;
private:
    ~C(){} // not public destructor!
};

class C_container{
    C c;
};

void f(){
    C_container* c_cont = new C_container;
    delete c_cont; // Compile ERROR: C has no accessible destructor
}
c ++ 11

C ++ 11에서 개발자는 컴파일러가 기본 소멸자를 제공하지 못하게함으로써이 동작을 무시할 수 있습니다.

class C{
    int m;
public:
    ~C() = delete; // does NOT have implicit destructor
};

void f{
    C c1; 
} // Compile ERROR: C has no destructor

또한 개발자는 컴파일러에서 기본 소멸자를 제공하도록 명시 할 수도 있습니다.

class C{
    int m;
public:
    ~C() = default; // saying explicitly it does have implicit/empty destructor
};

void f(){
    C c1;
} // C has a destructor -- c1 properly destroyed
c ++ 11

<type_traits> std::is_destructible 을 사용하여 유형에 소멸자가 있는지 (또는 기본 유형인지) 확인할 수 있습니다.

class C1{ };
class C2{ public: ~C2() = delete };
class C3 : public C2{ };

using std::cout; using std::boolalpha; using std::endl;
using std::is_destructible;
cout << boolalpha << is_destructible<int>() << endl; // prints true
cout << boolalpha << is_destructible<C1>() << endl; // prints true
cout << boolalpha << is_destructible<C2>() << endl; // prints false
cout << boolalpha << is_destructible<C3>() << endl; // prints false


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow