추상 기본 클래스 및 사본 구성, 규칙


10

종종 객체의 인터페이스를 분리하기 위해 추상 기본 클래스를 갖는 것이 좋습니다.

문제는 CHO에서는 복사 생성 (IMHO)이 기본적으로 거의 깨져 있고 기본적으로 복사 생성자가 생성된다는 것입니다.

그렇다면 파생 클래스에 추상 기본 클래스와 원시 포인터가있을 때의 문제점은 무엇입니까?

class IAbstract
{
    ~IAbstract() = 0;
}

class Derived : public IAbstract
{
    char *theProblem;
    ...
}

IAbstract *a1 = new Derived();
IAbstract a2 = *a1;//???

이제 전체 계층 구조에 대해 복사 구성을 완전히 비활성화합니까? 에서 사본 구성을 비공개로 선언 IAbstract하시겠습니까?

추상 기본 클래스에 세 가지 규칙이 있습니까?


1
포인터 대신 참조를 사용하십시오 :)
tp1

@ tp1 : 또는 적어도 일부 스마트 포인터.
Benjamin Bannier

1
때로는 기존 코드로 작업해야 할 수도 있습니다. 모든 것을 즉시 변경할 수는 없습니다.
Coder

왜 기본 복사 생성자가 손상되었다고 생각합니까?
BЈовић

2
@Coder : Google 스타일 가이드는 쓰레기 더미이며 모든 C ++ 개발에 절대적으로 짜증납니다.
DeadMG

답변:


6

대부분의 경우 할당 연산자뿐만 아니라 추상 클래스의 복사 구성도 비공개로 만들어야합니다.

추상 클래스는 정의에 따라 다형성 유형으로 만들어집니다. 따라서 인스턴스가 사용중인 메모리 양을 알 수 없으므로 안전하게 복사하거나 할당 할 수 없습니다. 실제로, 당신은 슬라이싱 위험이 있습니다 : https : //.com/questions/274626/what-is-the-slicing-problem-in-c

C ++에서 다형성 유형은 값으로 조작해서는 안됩니다. 참조 또는 포인터 (또는 스마트 포인터)로 조작 할 수 있습니다.

이것이 Java가 객체를 참조로만 조작 할 수있게 만든 이유와 C #과 D가 클래스와 구조체 (첫 번째는 다형성 및 참조 유형, 두 번째는 비다 형성 및 값 유형)로 구분되는 이유입니다.


2
Java의 상황이 더 나아지지 않습니다. Java에서는 실제로 필요한 경우에도 잊어 버리는 경우에도 무엇이든 복사하는 것이 고통 스럽습니다. 따라서 두 데이터 구조에 동일한 값 클래스 (예 : 날짜)에 대한 참조가있는 불쾌한 버그가 생겨서 그 중 하나가 날짜를 변경하고 다른 데이터 구조가 손상되었습니다.
케빈 클라인

3
Meh. C ++을 아는 사람은이 실수를하지 않을 것입니다. 더 중요한 것은, 당신이하고있는 일을 알고 있다면 값으로 다형성 유형을 조작 할 이유가 전혀 없다는 것입니다. 당신은 기술적으로 슬림 한 가능성에 대해 총체적인 무질서한 반응을 시작하고 있습니다. NULL 포인터는 더 큰 문제입니다.
DeadMG

8

물론 파생 클래스가 선택할 수 있도록 보호하고 비워 둘 수 있습니다. 그러나 더 일반적으로 코드는 순수한 가상 기능을 가지고 있기 때문에 인스턴스화가 불가능하기 때문에 어쨌든 금지되어 IAbstract있습니다. 따라서 이것은 일반적으로 문제가되지 않으므로 인터페이스 클래스를 인스턴스화 할 수 없으므로 복사 할 수 없으므로 파생 클래스는 원하는대로 복사를 계속하거나 금지 할 수 있습니다.


그리고 Abstract-> Derived1-> Derived2 계층이 있고 Derived2가 Derived1에 할당되면 어떻게 될까요? 이 언어의 어두운 구석은 여전히 ​​나를 놀라게합니다.
Coder

@ 코더 : 일반적으로 PEBKAC로 간주됩니다. 그러나 더 직접적으로에서에서 개인 operator=(const Derived2&)을 항상 선언 할 수 Derived1있습니다.
DeadMG

좋아, 아마도 이것은 실제로 문제가되지 않을 것입니다. 나는 학급이 학대에 대해 안전한지 확인하고 싶습니다.
Coder

2
그리고 당신은 메달을 받아야합니다.
세계 엔지니어

2
@ 코더 : 누구에 의한 학대? 최선을 다하면 올바른 코드를 쉽게 작성할 수 있습니다. C ++을 모르는 프로그래머를 방어하려고하는 것은 무의미합니다.
케빈 클라인

2

ctor와 할당을 비공개로 만들거나 C ++ 11에서 = delete로 선언하여 복사를 비활성화합니다.

여기서 요점은 어디에서해야하는지입니다. 코드를 유지하기 위해 IAbstract는 문제가되지 않습니다. (수행 한 작업을 수행하면 *a1 IAbstract하위 객체를 a2에 할당하고 참조를 잃어 버립니다 Derived. 값 할당은 다형성이 아닙니다)

문제는와 함께 제공됩니다 Derived::theproblem. Derived를 다른 사람에게 복사하면 실제로 공유 *theproblem하도록 설계되지 않은 데이터를 공유 할 수 있습니다 ( delete theproblem소멸자를 호출 할 수있는 두 개의 인스턴스가 있음 ).

이 경우 Derived복사 할 수없고 할당 할 수 없어야합니다. 물론을 (를) 비공개로 만들면 IAbstract사본이 Derived필요하기 때문에 기본 사본도 복사 Derived할 수 없습니다. 그러나 사본 Derived::Derived(const Derived&)을 호출하지 않고 자신을 정의하더라도 IAbtract여전히 복사 할 수 있습니다.

문제는 Derived에 있으며 솔루션은 Derived에 머물러 있어야합니다. 포인터 나 참조로만 액세스 할 수있는 동적 전용 개체 여야하는 경우에는 반드시 파생되어야합니다.

class Derived
{
    ...
    Derived(const Derived&) = delete;
    Derived& operator=(const Derived&) = delete;
};

기본적으로 Derived 클래스의 설계자 (Derived의 작동 방식 및 theproblem관리 방법을 알아야 함 )는 할당 및 복사로 수행 할 작업을 결정합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.