기본 클래스 (또는 인터페이스 클래스)를 선언하고 하나 이상의 매개 변수에 대한 기본값을 지정하면 파생 클래스가 동일한 기본값을 지정해야하며 그렇지 않은 경우 파생 클래스에서 어떤 기본값이 나타 납니까?
부록 : 또한이 시나리오에서 다른 컴파일러와 "권장"실습에 대한 입력을 처리하는 방법에 관심이 있습니다.
기본 클래스 (또는 인터페이스 클래스)를 선언하고 하나 이상의 매개 변수에 대한 기본값을 지정하면 파생 클래스가 동일한 기본값을 지정해야하며 그렇지 않은 경우 파생 클래스에서 어떤 기본값이 나타 납니까?
부록 : 또한이 시나리오에서 다른 컴파일러와 "권장"실습에 대한 입력을 처리하는 방법에 관심이 있습니다.
답변:
가상에는 기본값이있을 수 있습니다. 기본 클래스의 기본값은 파생 클래스에 의해 상속되지 않습니다.
기본 클래스 또는 파생 클래스와 같이 사용되는 기본값은 함수를 호출하는 데 사용되는 정적 유형에 의해 결정됩니다. 기본 클래스 객체, 포인터 또는 참조를 통해 호출하면 기본 클래스에 표시된 기본값이 사용됩니다. 반대로 파생 클래스 개체, 포인터 또는 참조를 통해 호출하면 파생 클래스에 표시된 기본값이 사용됩니다. 이를 나타내는 표준 인용 아래에 예가 있습니다.
일부 컴파일러는 다른 작업을 수행 할 수 있지만 C ++ 03 및 C ++ 11 표준은 다음과 같이 말합니다.
8.3.6.10 :
가상 함수 호출 (10.3)은 객체를 나타내는 포인터 또는 참조의 정적 유형에 의해 결정된 가상 함수 선언에서 기본 인수를 사용합니다. 파생 클래스의 재정의 함수는 재정의하는 함수에서 기본 인수를 얻지 않습니다. 예:
struct A { virtual void f(int a = 7); }; struct B : public A { void f(int a); }; void m() { B* pb = new B; A* pa = pb; pa->f(); //OK, calls pa->B::f(7) pb->f(); //error: wrong number of arguments for B::f() }
다음은 기본 설정을 보여주는 샘플 프로그램입니다. 나는 간결성을 위해 es struct
대신에 s를 사용 하고 있으며 기본 가시성을 제외하고 거의 모든면에서 정확히 동일합니다.class
class
struct
#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>
using std::stringstream;
using std::string;
using std::cout;
using std::endl;
struct Base { virtual string Speak(int n = 42); };
struct Der : public Base { string Speak(int n = 84); };
string Base::Speak(int n)
{
stringstream ss;
ss << "Base " << n;
return ss.str();
}
string Der::Speak(int n)
{
stringstream ss;
ss << "Der " << n;
return ss.str();
}
int main()
{
Base b1;
Der d1;
Base *pb1 = &b1, *pb2 = &d1;
Der *pd1 = &d1;
cout << pb1->Speak() << "\n" // Base 42
<< pb2->Speak() << "\n" // Der 42
<< pd1->Speak() << "\n" // Der 84
<< endl;
}
이 프로그램의 출력 (MSVC10 및 GCC 4.4)은 다음과 같습니다.
Base 42
Der 42
Der 84
이것은 Herb Sutter의 금주 의 초기 전문가 중 하나의 주제였습니다. 게시물 .
그가 그 주제에 대해 가장 먼저 말한 것은하지 말아야한다는 것입니다.
보다 상세하게 예, 다른 기본 매개 변수를 지정할 수 있습니다. 그들은 가상 기능과 같은 방식으로 작동하지 않습니다. 가상 함수는 객체의 동적 유형에서 호출되는 반면 기본 매개 변수 값은 정적 유형을 기반으로합니다.
주어진
class A {
virtual void foo(int i = 1) { cout << "A::foo" << i << endl; }
};
class B: public A {
virtual void foo(int i = 2) { cout << "B::foo" << i << endl; }
};
void test() {
A a;
B b;
A* ap = &b;
a.foo();
b.foo();
ap->foo();
}
A :: foo1 B :: foo2 B :: foo1을 가져와야합니다.
기본 인수 는 객체 의 정적 유형에 따라 결정 되지만 virtual
전달 된 함수는 동적에 따라 달라 지기 때문에 이것은 나쁜 생각 입니다. 유형 입니다.
즉, 기본 인수로 함수를 호출하면 함수의 유무에 관계없이 컴파일시 기본 인수가 대체됩니다 virtual
.
@cppcoder는 그의 [닫힌] 질문 에서 다음 예제를 제공했습니다 .
struct A {
virtual void display(int i = 5) { std::cout << "Base::" << i << "\n"; }
};
struct B : public A {
virtual void display(int i = 9) override { std::cout << "Derived::" << i << "\n"; }
};
int main()
{
A * a = new B();
a->display();
A* aa = new A();
aa->display();
B* bb = new B();
bb->display();
}
다음과 같은 출력이 생성됩니다.
Derived::5
Base::5
Derived::9
위의 설명을 통해 이유를 쉽게 알 수 있습니다. 컴파일시 컴파일러는 정적 유형의 포인터 멤버 함수에서 기본 인수를 대체하여 함수를 main
다음과 같습니다.
A * a = new B();
a->display(5);
A* aa = new A();
aa->display(5);
B* bb = new B();
bb->display(9);
다른 답변에서 볼 수 있듯이 이것은 복잡한 주제입니다. 이것을 시도하거나 그것이 무엇을하는지 이해하는 대신 (지금 요청해야하는 경우, 관리자는 1 년 후에 요청하거나 조회해야합니다).
대신 기본 클래스에서 기본 매개 변수를 사용하여 공개 비가 상 함수를 만듭니다. 그런 다음 기본 매개 변수가없는 개인용 또는 보호 된 가상 기능을 호출하고 필요에 따라 하위 클래스에서 대체됩니다. 그런 다음 어떻게 작동하는지에 대해 걱정할 필요가 없으며 코드가 매우 분명합니다.
이것은 아마도 테스트를 통해 합리적으로 잘 이해할 수있는 것입니다 (즉, 대부분의 컴파일러가 거의 확실하게 올바른 언어를 얻는 주류 부분입니다. 컴파일러 간의 차이점을 보지 않으면 출력은 권위있는 것으로 간주 될 수 있습니다).
#include <iostream>
struct base {
virtual void x(int a=0) { std::cout << a; }
virtual ~base() {}
};
struct derived1 : base {
void x(int a) { std:: cout << a; }
};
struct derived2 : base {
void x(int a = 1) { std::cout << a; }
};
int main() {
base *b[3];
b[0] = new base;
b[1] = new derived1;
b[2] = new derived2;
for (int i=0; i<3; i++) {
b[i]->x();
delete b[i];
}
derived1 d;
// d.x(); // won't compile.
derived2 d2;
d2.x();
return 0;
}
다른 답변에서 자세히 설명했듯이 나쁜 생각입니다. 그러나 간단하고 효과적인 해결책에 대해서는 아무도 언급하지 않았으므로 다음과 같습니다. 매개 변수를 struct로 변환 한 다음 struct 멤버에 대한 기본값을 가질 수 있습니다!
대신에
//bad idea
virtual method1(int x = 0, int y = 0, int z = 0)
이 작업을 수행,
//good idea
struct Param1 {
int x = 0, y = 0, z = 0;
};
virtual method1(const Param1& p)