문제

예, 나는 그들 사이의 차이점을 이해합니다.내가 알고 싶은 것은:왜 메소드를 재정의합니까?그렇게 하면 어떤 점이 좋은가요?과부하가 발생한 경우:유일한 장점은 함수에 대해 다른 이름으로 생각할 필요가 없다는 것입니다.

도움이 되었습니까?

해결책

과부하 일반적으로 동일한 이름을 가진 동일한 범위에 두 개 이상의 함수가 있음을 의미합니다.호출 시 인수와 더 잘 일치하는 함수가 승리하여 호출됩니다.가상 함수를 호출하는 것과 달리 주목해야 할 중요한 점은 호출되는 함수가 컴파일 타임에 선택된다는 것입니다.그것은 모두 인수의 정적 유형에 따라 다릅니다.과부하가 걸린 경우 B 그리고 하나는 D, 인수는 다음을 참조합니다. B, 하지만 실제로는 다음을 가리킵니다. D 객체에 대한 과부하가 발생합니다. B C++에서 선택되었습니다.그것은 호출됩니다 정적 파견 반대로 동적 파견.동일한 이름을 가진 다른 함수와 동일한 작업을 수행하고 싶지만 다른 인수 유형에 대해 해당 작업을 수행하려는 경우 오버로드합니다.예:

void print(Foo const& f) {
    // print a foo
}

void print(Bar const& bar) {
    // print a bar
}

둘 다 인수를 인쇄하므로 오버로드됩니다.그러나 첫 번째는 foo를 인쇄하고 두 번째는 bar를 인쇄합니다.서로 다른 작업을 수행하는 두 개의 함수가 있는 경우 이름이 같으면 잘못된 스타일로 간주됩니다. 왜냐하면 함수를 호출할 때 실제로 어떤 일이 일어날지에 대한 혼란을 초래할 수 있기 때문입니다.오버로드의 또 다른 사용 사례는 함수에 대한 추가 매개변수가 있지만 제어를 다른 함수로 전달하는 경우입니다.

void print(Foo & f, PrintAttributes b) { 
    /* ... */ 
}

void print(Foo & f, std::string const& header, bool printBold) {
    print(f, PrintAttributes(header, printBold));
}

오버로드가 사용하는 옵션이 자주 사용되는 경우 호출자에게 편리할 수 있습니다.

재정의 완전히 다른 것입니다.과부하와 경쟁하지 않습니다.이는 기본 클래스에 가상 함수가 있는 경우 파생 클래스에서도 동일한 시그니처를 사용하여 함수를 작성할 수 있음을 의미합니다.파생 클래스의 함수 재정의 베이스의 기능.견본:

struct base {
    virtual void print() { cout << "base!"; }
}

struct derived: base {
    virtual void print() { cout << "derived!"; }
}

이제 객체가 있고 print 멤버 함수에서 파생된 인쇄 함수는 기본 함수를 재정의하므로 항상 호출됩니다.기능의 경우 print 가상이 아닌 경우 파생된 함수는 기본 함수를 재정의하지 않고 단지 숨다 그것.기본 클래스와 그 클래스에서 파생된 모든 클래스를 허용하는 함수가 있는 경우 재정의가 유용할 수 있습니다.

void doit(base &b) {
    // and sometimes, we want to print it
    b.print();
}

이제 컴파일 타임에 컴파일러는 b가 최소한 base라는 것만 알고 있지만 파생 클래스의 print가 호출됩니다.이것이 바로 가상 함수의 핵심입니다.이것이 없으면 기본의 인쇄 기능이 호출되고 파생 클래스의 인쇄 기능이 이를 재정의하지 않습니다.

다른 팁

이것은 생각에 더 명확성을 더할 것입니다.enter image description here

세 가지 이유의 기능 :

  1. 유사하고 밀접하게 관련된 것들을 수행하는 두 가지 (또는 그 이상) 기능을 제공합니다. 고안 예 :

    void Log(std::string msg); // logs a message to standard out
    void Log(std::string msg, std::ofstream); // logs a message to a file
    
  2. 동일한 동작을 수행하는 두 가지 (또는 그 이상) 방법을 제공합니다. 고안 예 :

    void Plot(Point pt); // plots a point at (pt.x, pt.y)
    void Plot(int x, int y); // plots a point at (x, y)
    
  3. 두 가지 (또는 그 이상의) 다른 입력 유형이 주어진 동등한 동작을 수행 할 수있는 능력을 제공합니다. 고안 예 :

    wchar_t      ToUnicode(char c);
    std::wstring ToUnicode(std::string s);
    

~ 안에 약간 사례는 다른 이름의 함수가 과부하 된 기능보다 더 나은 선택이라고 주장 할 가치가 있습니다. 생성자의 경우 과부하가 유일한 선택입니다.


위에승마 함수는 완전히 다르며 완전히 다른 목적을 제공합니다. 기능 재정의는 다형성이 C ++에서 어떻게 작동하는지입니다. 파생 클래스에서 해당 함수의 동작을 변경하기 위해 함수를 무시합니다. 이러한 방식으로 기본 클래스는 인터페이스를 제공하고 파생 클래스는 구현을 제공합니다.

오버라이드는 기본 클래스에서 상속하고 기능을 확장하거나 수정하려는 경우 유용합니다. 객체가 기본 클래스로 캐스팅 되더라도 기본 기능이 아닌 재정의 함수라고합니다.

과부하가 필요하지는 않지만 때로는 삶이 더 쉬워 지거나 더 쉽게 읽을 수 있습니다. 아마도 그것은 그것을 악화시킬 수 있지만, 그때는 사용해서는 안됩니다. 예를 들어, 동일한 작업을 수행하는 두 가지 기능을 가질 수 있지만 다른 종류의 일에 작용할 수 있습니다. 예를 들어 Divide(float, float) 다른 것과 달라야합니다 Divide(int, int), 그러나 그들은 기본적으로 동일한 작업입니다. "DivideFloat", "DivideInt", "DivideIntByFloat"등을 기억하는 것보다 하나의 방법 이름 인 "Divide"를 기억하지 않습니까?

사람들은 이미 과부하와 재정의를 모두 정의 했으므로 정교하지 않을 것입니다.

금고 질문 :

과부하의 유일한 장점]은 여러 이름으로 함수에 대해 생각하지 않았습니까?

1. 여러 이름으로 생각할 필요가 없습니다

그리고 이것은 이미 강력한 이점입니다.

알려진 C API 기능 및 허구의 C ++ 변형과 비교해 봅시다.

/* C */
double fabs(double d) ;
int abs(int i) ;

// C++ fictional variants
long double abs(long double d) ;
double abs(double d) ;
float abs(float f) ;
long abs(long i) ;
int abs(int i) ;

이것은 두 가지를 의미합니다. 하나는 컴파일러에게 올바른 함수를 선택하여 기능에 공급되는 데이터 유형을 알려야합니다. 둘째, 확장하려면 멋진 이름을 찾아야하며 기능 사용자는 올바른 멋진 이름을 기억해야합니다.

그리고 그녀가 원하는 것은 숫자 변수의 절대 값을 갖는 것이 었습니다 ...

하나의 동작은 하나의 기능 이름을 의미합니다.

하나의 매개 변수의 유형을 변경하는 데 제한되지 않습니다. 의미가있는 한 모든 것이 변할 수 있습니다.

2. 운영자에게는 필수입니다

운영자를 보자 :

// C++
Integer operator + (const Integer & lhs, const Integer & rhs) ;
Real operator + (const Real & lhs, const Real & rhs) ;
Matrix operator + (const Matrix & lhs, const Matrix & rhs) ;
Complex operator + (const Complex & lhs, const Complex & rhs) ;

void doSomething()
{
   Integer i0 = 5, i1 = 10 ;
   Integer i2 = i0 + i1 ; // i2 == 15

   Real r0 = 5.5, r1 = 10.3 ;
   Real r2 = r0 + r1 ; // r2 = 15.8

   Matrix m0(1, 2, 3, 4), m1(10, 20, 30, 40) ;
   Matrix m2 = m0 + m1 ; // m2 == (11, 22, 33, 44)

   Complex c0(1, 5), c1(10, 50) ;
   Complex c2 = c0 + c1 ; // c2 == (11, 55)
}

위의 예에서, 당신 원해 + 연산자 이외의 다른 사용을 피하기 위해.

C는 내장 유형 (C99 복합 유형 포함)에 대한 암시 적 연산자 과부하가 있습니다.

/* C */
void doSomething(void)
{
   char c = 32 ;
   short s = 54 ;
   c + s ; /* == C++ operator + (char, short) */
   c + c ; /* == C++ operator + (char, char) */
}

따라서 객관적이지 않은 언어 에서도이 과부하가 사용됩니다.

3. 물체의 경우 필수입니다

객체 기본 방법의 사용을 보자 : 생성자 :

class MyString
{
   public :
      MyString(char character) ;
      MyString(int number) ;
      MyString(const char * c_style_string) ;
      MyString(const MyString * mySring) ;
      // etc.
} ;

일부는이 기능과 같은 기능 과부하를 고려할 수 있지만 실제로는 작업자 과부하와 더 유사합니다.

void doSomething()
{
   MyString a('h') ;                  // a == "h" ;
   MyString b(25) ;                   // b == "25" ;
   MyString c("Hello World") ;        // c == "Hello World" ;
   MyString d(c) ;                    // d == "Hello World" ;
}

결론 : 과부하가 멋지다

C에서는 함수의 이름을 줄 때 매개 변수는 호출시 서명의 암시 적으로 일부입니다. "Double Fab (Double D)"이 있으면 컴파일러의 팹의 시그니처가 미개화 된 "팹"이지만 두 배만 걸리는 것을 알아야합니다.

C ++에서 함수의 이름이 서명이 강제되었음을 의미하지는 않습니다. 통화시의 서명은 이름과 매개 변수입니다. 따라서 ABS (-24)를 작성하면 컴파일러는 호출 해야하는 ABS의 과부하를 알 수 있으며, 글을 쓸 때 더 자연스럽게 찾을 수 있습니다. -24의 절대 값을 원합니다.

어쨌든, 운영자가있는 언어로 어떤 언어로 코딩 한 사람은 이미 C 또는 기본 수치 연산자, Java 문자열 연결, C# 대표 등을 사용하고 있습니다. 왜 그런가? 더 자연 스럽기 때문입니다.

위에 표시된 예제는 빙산의 끝 일뿐입니다. 템플릿을 사용할 때 과부하가 매우 유용하지만 이것은 또 다른 이야기입니다.

교과서 예제는 메소드 speak ()가있는 클래스 동물입니다. 개 서브 클래스는 speak ()를 "껍질"으로 무시하고 고양이 서브 클래스는 speak ()를 "Meow"로 무시합니다.

과부하를 한 번 사용하는 것은 템플릿에서 사용하는 것입니다. 템플릿에서는 다른 데이터 유형에 사용할 수있는 코드를 작성하고 다른 유형으로 호출합니다. 다른 인수를 취하는 기능이 다르게 명명되어야한다면, 다른 데이터 유형에 대한 코드는 일반적으로 달라야하며 템플릿은 작동하지 않습니다.

아직 템플릿을 작성하지는 않지만 거의 확실히 일부 템플릿을 사용하고 있습니다. 스트림은 템플릿이며 벡터도 마찬가지입니다. 과부하가 발생하지 않고 템플릿이 없으면 ASCII 스트림과 다른 유니 코드 스트림을 호출해야하며 벡터 대신 배열과 포인터를 사용해야합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top