가상 함수에 기본 인자가 있습니까?

그래, 그들은 할 수 있지만, 너는 그들에게 의존해서는 안 된다. 왜냐하면 너는 네가 원하는 것을 얻지 못할 수도 있기 때문이다.
만약 당신이 이 화제가 어떻게 발생했는지 알고 싶다면, 답은 정적 코드 분석입니다!여러 해 동안 우리는 정적 코드 분석기를 사용해 왔다. 조금씩 접촉 부분을 정리하고 보이스카우트 규칙을 응용함으로써 우리는 가장 심각한 위반자를 제거했다.
최악의 상황은 어느 정도 분석기의 작업 방식에 달려 있다.너는 그 중의 일부 건의에 동의하지 않을 수도 있지만, 만약 이 건의들을 자주 본다면, 너는 그것들을 복원하고 추가하는 것을 멈추기 시작할 것이다.
물론, 너는 이 차의 승객이 될 필요가 없다.너는 가능한 한 운전사가 되어야 한다.회사 차원에서, 이것은 사용자가 자신의 필요에 따라 분석기에서 사용하는 프로필을 맞춤형으로 만들어야 한다는 것을 의미한다.
에서 말한 바와 같이, 이것은 업계 표준 설정 파일에 규칙을 추가하는 것이지 삭제하는 것이 아니라는 것을 의미합니다.
저희 회사에서 최근에 저희 코드 라이브러리에 새로운 품질 프로필을 적용했습니다. 이로 인해 수천 개의 새로운 규칙 위반 행위가 발생했습니다. 단기, 중기, 최선을 다해 복원하고 싶은지에 따라 분류하기 시작했습니다.
만약에 우리가 개요 파일을 응용한 후에 분류를 하는 이유를 알고 싶다면, 우리는 개요 파일을 만들지 않았지만, 우리는 창설자에게 가치 있는 피드백을 제공하고, 우리 팀원들에게 해결 방안을 제공하기를 바란다.
앞으로 몇 개월 동안, 나는 당신들과 우리가 발견한 몇 가지 가장 재미있는 규칙을 공유할 것입니다.

The problem of default arguments
가상 함수에서 기본 매개 변수의 초기값 설정 항목을 사용하는 것은 문법적으로 정확하지만, 시간의 추이에 따라 코드는 유지보수되지 않을 가능성이 높다.이 동시에 발생하는 혼란은 부정확한 다중 코드와 클래스 차원 구조에서 불필요한 복잡성을 초래할 것이다.
예를 살펴보겠습니다.
#include <iostream>

class Base {
public:
  virtual void fun(int p = 42) {
    std::cout << p << std::endl;
  }
};

class DerivedLeft : public Base {
public:
  void fun(int p = 13) override {
    std::cout << p << std::endl;
  }
};

class DerivedRight : public Base {
public:
  void fun(int p) override {
    std::cout << p << std::endl;
  }
};
다음main 기능에 대한 기대는 무엇입니까?
int main() {
  DerivedLeft *d = new DerivedLeft;
  Base *b = d;
  b->fun();
  d->fun();
}
이 가능하다, ~할 수 있다,...
42
13
만약 그렇다면, 축하합니다!특히 우연이 아니었다면만약 네가 또 다른 것을 기대한다면 걱정하지 마라.이것은 뚜렷하지 않다. 이것은 가상 함수를 위해 기본 매개 변수 값을 사용하는 문제이다.b는 파생류를 가리키지만 Base의 기본값을 사용했다.
그럼 다음 가능한 상황main은요?
int main() {
  Base *b2 = new Base;
  DerivedRight *d2 = new DerivedRight;
  b2->fun();
  d2->fun();
}
너는 42회 연속을 기대할 수도 있지만, 이것은 잘못된 것이다.코드를 컴파일할 수 없습니다.다시 쓰기 함수는 기본값을 계승하지 않기 때문에 funDerivedRight 호출에 실패했습니다.
/*
main.cpp: In function 'int main()':
main.cpp:28:11: error: no matching function for call to 'DerivedRight::fun()'
   28 |   d2->fun();
      |           ^
main.cpp:19:8: note: candidate: 'virtual void DerivedRight::fun(int)'
   19 |   void fun(int p) override {
      |        ^~~
main.cpp:19:8: note:   candidate expects 1 argument, 0 provided
*/

정적 및 동적 유형
막후에서 일어난 일을 더 잘 이해하기 위해 한 걸음 뒤로 물러서자.원시 예시를 조금만 수정하고 잊어버리자DerivedRight.
#include <iostream>

class Base {
public:
  virtual void fun(int p = 42) {
    std::cout << "Base::fun " << p << std::endl;
  }
};

class Derived : public Base {
public:
  void fun(int p = 13) override {
    std::cout << "Derived::fun " << p << std::endl;
  }
};

int main() {
  Derived *derived = new Derived;
  derived->fun();
  Base *base = derived;
  base->fun();
}
당신이 지금 기대하는 생산량은 얼마입니까?
다음과 같습니다.
Derived::fun 13
Derived::fun 42
이 두 가지 상황에서 파생 버전이 호출되었지만 기본 파라미터가 다르다는 것을 알 수 있습니다.
대상의 동적 형식에 대해 허함수를 호출하고 기본 매개 변수 값은 정적 형식을 기반으로 합니다.이 두 가지 상황에서 동적 유형은 모두 Derived이지만 정적 유형이 다르기 때문에 서로 다른 기본값을 사용한다.

이게 정말 문제인가요?만약 그렇다면 어떡하지?
이것은 절대로 문법 문제가 아니다. 어쨌든 그것은 번역된 것이다.
주요 문제는 동적 형식을 사용하여 어떤 함수를 실행할지 결정할 때 코드가 오해하기 쉽고 기본 파라미터를 가져올 때 정적 형식을 사용합니다.
이런 복잡성을 피하고 기본 행동이 필요한 함수를 비가상으로 만드는 것이 좋다.
이를 실현하는 방법 중 하나는 보호된 이른바 전달 기능을 사용하는 것이다.
#include <iostream>

class Base {
public:
  void fun(int p = 42) {
    fun_impl(p);
  }
protected:
  virtual void fun_impl(int p) {
    std::cout << "Base::fun " << p << std::endl;
  }
};

class DerivedLeft : public Base {
protected:
  void fun_impl(int p) override {
    std::cout << "DerivedLeft::fun " << p << std::endl;
  }
};

class DerivedRight : public Base {
protected:
  void fun_impl(int p) override {
    std::cout << "DerivedRight::fun " << p << std::endl;
  }
};

int main() {
  DerivedLeft *d = new DerivedLeft;
  Base *b = d;
  DerivedRight *d2 = new DerivedRight;

  b->fun();
  d->fun();
  d2->fun();
}
이런 상황에서 실현만 바뀌었을 뿐 행위는 바로 사람들이 기대하는 것이다.
DerivedLeft::fun 42
DerivedLeft::fun 42
DerivedRight::fun 42
두 번째 기본 동작이 필요하다면, 상응하는 파생 클래스에 다른 비 가상 fun 함수를 만들고, 새로운 기본 매개 변수를 fun_impl 로 전송하면 작용할 것이다.
같은 차원 구조의 다른 종류에서 같은 서명을 사용하고 다른 것을 덮어쓰지 않는 것이 좋은 생각인지 의문도 있다.
가장 좋은 방법은 이런 서로 다른 기본 매개 변수를 사용하지 않는 것이다.

결론
정적 코드 분석기는 코드에 잠재되어 있는 버그를 복구하고 같은 유형의 코드에서 우리가 고려하지 않았을 수도 있는 미묘한 규칙과 사례를 팀에 전수하는 데 도움을 줄 수 있다.
오늘 우리는 가상 함수에 기본 인자를 사용하는 것은 나쁜 생각이다. 정적, 동적 형식이 혼합되어 있기 때문에 그때가 되면 유지보수의 부담이 될 것이다.
간단한 전송 기능을 통해 너는 이런 수요를 피할 수 있다.
정상적인 다중 행동과 비교되는 이러한 차이를 감안하면 가상 함수 중의 기본 매개 변수를 피하는 것이 가장 좋다.

더욱 깊이 연락하다
만약 당신이 이 문장이 재미있다고 생각한다면 subscribe to my personal blog계속 연결합시다!

좋은 웹페이지 즐겨찾기