가상 함수의 기본값의 위험!

5835 단어 cpp

기사::기사



다음은 친구가 보낸 코드 스니펫입니다.

#include <iostream>

class Base
{
    public:
        virtual void rick(int x = 0)
        {
            if (0 == x)
                std::cout << "Give you up\n";
            else
                std::cout << "Let you down\n";
        }
};

class Derived : public Base
{
    public:
        virtual void rick(int x = 10)
        {
            if (0 == x)
                std::cout << "Run around and desert you\n";
            else
                std::cout << "Make you cry\n";
        }
};

int main()
{
    Derived d1;
    Base* bp = &d1;

    std::cout << "Never gonna ";
    bp->rick();

    return 0;
}


Compiler explorer link
무엇을 인쇄할지 짐작할 수 있습니까? 바로 위의 컴파일러 탐색기 링크에서 솔루션을 볼 수 있습니다.

개인적으로 올바른 솔루션을 찾았지만 두 가지가 도움이 되었습니다. 첫째, 친구가 저를 속이려고 이 스니펫을 보낸다는 것을 알았습니다. 상속 및 기본값. 실제 프로젝트에서는 이것을 발견하는 것이 그렇게 간단하지 않을 수 있습니다.

이제 이것이 왜 이렇게 작동하는지 살펴보겠습니다!

설명



약간의 요약


Base라는 기본 클래스와 Base라는 이름의 Derived에서 상속받은 클래스가 있습니다. (원래 얼마나)virtual void Base::rick(int x = 0)에 가상 멤버 함수Base가 있고 Derived에 같은 이름을 가진 함수가 있습니다: virtual void Derived::rick(int = 10)두 멤버 함수 모두 x 값에 따라 다른 텍스트를 인쇄합니다.

기본 함수에는 Derived 멤버 함수를 호출하기 위해 Base*를 통해 액세스하는 rick 개체가 있습니다.

출력이 무엇인지 추측하려고 하면 네 가지 가능성이 있음을 의미합니다.
  • 절대 포기하지 않을거야
  • 절대 실망시키지 않을거야
  • 절대 뛰어다니고 널 버리지 않을거야
  • Never gonna Make you cry

  • 단계별 분석


    rick가 호출되고 rickBase에서 정의되고 Derived에서 재정의되는 것을 추론하는 것으로 시작하겠습니다. 예, 기본값이 프로토타입의 일부가 아닙니다.
    이제 어느 것이 호출되는지 아는 것이 매우 쉽다는 것을 알고 있습니다. x 객체가 있고 함수는 가상이며 포인터를 통해 호출하면 Derived를 호출합니다.
    두 가지 가능성이 남아 있습니다.
  • 절대 뛰어다니고 널 버리지 않을거야
  • Never gonna Make you cry

  • 기본 인수가 10인치virtual void Derived::rick이기 때문에 "Never gonna Make you cry"라고 대답하고 싶은 유혹을 느낄 수 있지만 이는 잘못된 것입니다. 실제 출력은 "Never gonna Run Around and Desert You"이고 여기서 유일한 버그는 의자와 키보드 :)

    왜 그렇게 합니까?



    표준이 그렇게 말하고 있기 때문입니다.

    The overriders of virtual functions do not acquire the default arguments from the base class declarations, and when the virtual function call is made, the default arguments are decided based on the static type of the object



    이는 문자 그대로 virtual void Derived::rick(int x = 10) 유형의 객체가 있고 Derived를 호출하는 경우 인수의 기본값이 호출되는 구현에 의존하지 않는다는 것을 의미합니다.

    기사::~기사



    보시다시피 동작은 논리적이지만 직관적이지 않고 파악하기 어려울 수 있습니다. 그렇기 때문에 이것을 사용해서는 안 된다고 생각하지만, 당신이 그것을 사용할 만큼 미쳤다면 다른 개발자들에게 이것에 대해 경고할 수 있는 주석과 문서가 있어야 합니다.

    출처


  • https://en.cppreference.com/w/cpp/language/default_arguments
  • 좋은 웹페이지 즐겨찾기