질문 배경:
C++는 현대 언어에 기본이 되는 객체 지향 프로그래밍 지원을 도입했습니다. 다형성을 구현하기 위해 가상 함수가 사용되었습니다. 이것은 상속을 가진 아키텍처에 중요하게 작용하는 방법을 컴파일 단계가 아닌 실행 단계에서 호출할 수 있게 해주었습니다.
문제점:
일반적인 오류는 정적 호출과 동적 호출 사이의 혼동, 잊혀진 가상 소멸자, 잘못된 상속 처리(예: 객체 슬라이스, 기본 버전 호출 대신 오버라이드 호출)입니다. 진정한 다형성이 작동할 때에 대한 혼동이 자주 발생합니다.
해결책:
가상 함수는 기본 클래스에서 virtual 키워드로 선언되며 파생 클래스에서 오버라이드될 수 있습니다. 기본 클래스의 포인터나 참조를 통해 함수를 호출하면 파생 클래스의 구현 버전이 실행됩니다.
코드 예제:
struct Base { virtual void foo() { std::cout << "Base::foo "; } }; struct Derived : Base { void foo() override { std::cout << "Derived::foo "; } }; void call(Base& b) { b.foo(); } int main() { Derived d; call(d); // 출력: Derived::foo }
주요 특징:
객체를 값으로 전달할 때 다형성이 작동합니까?
아니요. 값으로 전달하면 "슬라이스"가 발생하여 매개변수 유형에 해당하는 부분(보통 기본 클래스)만 복사되므로 다형성이 비활성화됩니다.
코드 예제:
void call(Base b) { b.foo(); } // 항상 Base::foo 호출
기본 클래스에서 소멸자를 가상으로 선언해야 합니까?
예, 기본 클래스에 대한 포인터를 통해 파생 객체를 삭제할 경우 그렇습니다. 그렇지 않으면 메모리 누수나 자원 해제가 발생합니다.
코드 예제:
struct Base { virtual ~Base() {} };
자식 클래스에서 override 키워드를 사용하지 않으면 어떤 일이 발생합니까?
파생 클래스에서 override를 지정하지 않고 함수의 시그니처를 잘못 변경하면(예: const를 빼먹거나 파라미터를 잘못 지정하면) 함수는 가상 함수를 오버라이드하지 않고 새롭게 생성되므로 다형성이 예상대로 작동하지 않습니다.
프로그래머가 기본 클래스의 소멸자를 가상으로 선언하지 않았습니다; 기본 포인터를 통해 객체 배열을 삭제하면 메모리 누수가 발생했습니다.
장점:
단점:
가상 소멸자가 선언되었고 기본 유형에 대한 포인터/참조만 사용되었습니다. 다형성이 올바르게 작동했습니다.
장점:
단점: