web-dev-qa-db-ja.com

純粋な仮想関数をデフォルトの引数でオーバーライドすることは良いですか悪いですか?

デフォルトの引数で純粋な仮想関数をオーバーライドすることは良いですか悪いですか?

class Base {
public:
    virtual int func(int i, int j = 10) = 0;
};

class Derived : public Base {
public:
    int func(int i, int j = 20) override;
};
3
msc

私はこれをするつもりはありません。 @ Bart van Ingen Schenaが言ったように、せいぜいwillはあなたに厄介な驚きを引き起こします。さらに、そもそも仮想関数でデフォルトのパラメーターを使用することもお勧めしません。あなたがそのようなものを持ちたいなら、私はこのようなことをすることを勧めます:

class Base {
public:
    virtual int func(int i, int j) = 0;
    virtual int func(int i)
    {
        func(i, 10);
    }
};

class Derived : public Base {
public:
    int override func(int i, int j)
    {
        //Some override code
    }

    int override func(int i)
    {
        func(i, 20);
    }
};

基本的な機能に最も具体的なメソッドを提供し、クラスを使用する開発者に簡単なメソッドを提供するため、パラメータのデフォルト値を気にする必要がありません。これにより、介入の柔軟性が高まり、呼び出されたメソッドとその方法を常に把握できます。

8
Vladimir Stokic

編集

それ[〜#〜] is [〜#〜] a ひどいアイデア!オーバーライドされたメソッドを呼び出しますが、基本クラスのデフォルトを取得します

c#コード

public class Base
{
    public virtual int Func(int i = 10)
    {
        Console.WriteLine("base");
        return i;
    }
}
public class Derived : Base
{
    public override int Func(int i = 20)
    {
        Console.WriteLine("derived");
        return i;
    }
}
Base b = new Derived();
Derived d = new Derived();
Console.WriteLine(b.Func());
Console.WriteLine(d.Func());

//ouput
derived
10
derived
20

c ++コード

    #include <iostream>
#include <string>

class Base {
public:
    virtual int func(int i = 10)
    {
        std::cout << "base";
        return i;
    };
};

class Derived : public Base {
public:
    int  func(int i = 20) override
    {
        std::cout << "derived";
        return i;
    };
};

int main()
{
    Base* b;
    Derived d;
    b = &d;
    std::cout << b->func();  
    std::cout << d.func(); 
}
//output 
derived10derived20 

クレイジー!

3
Ewan

原則として、関数をdefault-argumentsでオーバーライドしても問題はありません。

実際には、オーバーライドに異なるデフォルトが含まれる可能性が非常に高く、これが混乱と混乱の大きな原因です。特に、変更されたデフォルトが異なる動作につながる場合、コードはオブジェクトだけでなく、呼び出し側の知識にも依存します。

したがって、デフォルトで関数をオーバーライドすることは避けてください。本当にそうする必要がある場合は、オーバーライドされた関数にデフォルトがあった場合とまったく同じデフォルトを提供してください。ただし、デフォルトを追加することもできます。
オーバーライド可能な保護された関数にデフォルトで転送するオーバーライド不可能な関数を用意するだけで、簡単に、そしてほとんどの場合それほど効率が悪くなり、その間違いを防ぐことができます。
(フォワーダーが何らかの理由でインライン化できない場合は効率が低下します。)


問題を見る別の方法は、デフォルトの関数を単一の関数としてではなく、全体のオーバーロードとして見ることです。そのうちの1つだけがオーバーライド可能で、アドレスを取得できます。

これらのオーバーロードの一部が欠落しているか別のことをしていると、オーバーライドされた関数がベースに違反しているのと同じように、静的タイプの Liskov Substitution Principle の違反になります(テンプレートとコードを直接記述する場合に重要) -class-contractは、静的タイプと動的タイプの両方に対応します。

3
Deduplicator

これは非常に悪い考えであり、その理由は次のとおりです。

オブジェクトのインスタンス(特に、参照またはポインタを使用して参照するインスタンス)がある場合、declaredクラスがあります(X * pを宣言した場合、* pは、 Xのインスタンス)、それは実際の型です(YがXのサブクラスで、p = new Y()を割り当てた場合、* pは実際にはYのインスタンスです。

オーバーライドされた非仮想関数を呼び出すと、コンパイラは宣言された型に従って関数を呼び出します。それはしばしば間違っています。仮想関数を使用すると、コンパイラーは正しい実装(Yの実装であり、すべてが正常に動作します)を呼び出します。

デフォルト値を変更すると、両方が混在することになりますが、これは可能な限り最悪のことです。コンパイラーは実際のクラスに属する関数を呼び出しますが、宣言されたクラスに従ってデフォルトのパラメーターを渡します。

PをX *またはY *として宣言しても違いはありませんが、突然違います。別のデフォルト引数が使用されます。

引数を非デフォルトにして、その引数なしで別の仮想関数を追加します。これにより、Origin関数がデフォルトの引数であるはずのものが渡され、必要に応じてオーバーライドされます。

0
gnasher729