5


1

C ++継承、隠された基本メソッド

単純なC ++基本クラス、派生クラスの例があります。

// Base.hpp
#pragma once

class Base
{
public:
    virtual float getData();
    virtual void setData(float a, float b);
    virtual void setData(float d);

protected:
    float data;
};

//Base.cpp
#include "stdafx.h"
#include "Base.hpp"

float Base::getData()
{
    return data;
}

void Base::setData(float a, float b)
{
    setData(a);
}

void Base::setData(float d)
{
    data = d;
}

//Derived.hpp
#pragma once
#include "Base.hpp"

class Derived
    : public Base
{
public:
    virtual void setData(float d);
};

//Derived.cpp
#include "stdafx.h"
#include "Derived.hpp"

void Derived::setData(float d)
{
    data = d + 10.0f;
}

今、ベースへのポインタを作成すると、これはうまくコンパイルされます。

//Main.cpp
#include "stdafx.h"
#include "Base.hpp"
#include "Derived.hpp"

Base *obj = new Derived();

しかし、Derivedクラスへのポインターを作成すると、コンパイラー(VC 2008および2010)は次のように文句を言います。

Main.cpp(12): error C2660: 'Derived::setData' : function does not take 2 arguments

そして、このエラーを引き起こすコードは次のとおりです。

//Main.cpp
#include "stdafx.h"
#include "Base.hpp"
#include "Derived.hpp"

Derived *obj = new Derived();

基本クラスのメソッドは隠されているようです。 基本クラスのメソッドは仮想であるため、Derivedポインターから見た場合でも表示されるはずだという印象を受けましたか、それとも間違っていますか?

2 Answer


11


これは、C ++名前検索の成果物です。 基本的なアルゴリズムは、コンパイラーが現在の値の型から開始し、ターゲット名を持つ型のメンバーが見つかるまで階層を上に進みます。 次に、指定された名前を持つその型のメンバーのみでオーバーロード解決を行います。 親タイプの同じ名前のメンバーは考慮されません。

これを回避する方法は、 Derived`で関数を再定義し、それらを Base`に転送することです。

class Derived {
  ...
  void setData(float a, float b);
}

void Derived::setData(float a, float b) {
  Base::setData(a,b);
}

さらに、「using」宣言を使用してベースメンバーをスコープに入れることができます

class Derived {
  using Base::setData;
  ...
}

この使用方法に関するドキュメント


3


派生クラスの関数は、基本クラスの同じ名前の関数を隠します。

その名前の基本クラス関数を本当に表示したい場合は、using宣言を追加してそれを実現できます。

class Base {
public:
    void setData(float d);
    void setData(float d, float e);
};

class Derived : public Base {

    using Base::setData;

    void SetData(float d);
};

[Revised:] Seems my memory was wrong. This doesn’t cause an ambiguity — using宣言を使用した場合でも、 Derived :: setData`が(いわば)優先されるため、派生オブジェクトで setData(float) を呼び出すと、 Derived :: setData(float) `が取得されます。 仮想化し、Baseへのポインターを介して呼び出す場合、引き続き `Derived :: setData(float);`を取得します。