簡體   English   中英

C ++繼承了模塊化函數

[英]C++ subclasses modular functions

抱歉,由於代碼充滿了我的錯誤並且沒有傳達我的觀點,所以我決定從根本上更新問題。

我想看看以下內容是否可行:

這是一個可以編譯的示例:

#include <stdio.h>

class TestA
{
    public:
        int A;

        TestA(){A = 1;}
        const TestA &operator=(const TestA &Copy)
        {
            A = Copy.A;
        }
};

class TestB : public TestA
{
    public:
        using TestA::operator=;
        void AdvancedFunction1(){A = A + A;}
};



int main()
{
    TestA Test;
    TestB *Alt;
    TestB Alt2;

    Alt = (TestB *)&Test;

    printf("%d!\n",Test.A);
    Alt->AdvancedFunction1();

    printf("%d!\n",Test.A);

    Test = Alt2;

    printf("%d!\n",Test.A);

    return 0;
}

已知,TestB可以(通過強制轉換)指向TestA,並且可以成功修改TestA,這是否可行? 看來如此,有什么陷阱? (我知道人們說這是錯誤的,但是除了“約定”以外,還有什么特別錯誤的地方?)。

您正在做的事情稱為“ 向上轉換” 向上轉換是當您使用指向派生類型的指針並嘗試將其分配給基本類型時。 沒用

只允許將指針隱式轉換為其基類之一的類型的指針。 您可以將TestB*TestC* TestA*轉換為TestA* ,但是不能將TestB*隱式TestA*轉換為TestC* ,也不TestA*隱式TestA*為其任意子類。 因此,您的示例甚至無法編譯。

原因是,例如在您的示例中,如果您可以將基本指針轉換為派生指針,則可以在基本指針上調用派生函數,這會引起很多問題,因為您將嘗試訪問派生成員變量,而它們不會在那里。

您可以通過顯式強制轉換來執行此操作,但是如果您這樣做,則如果基礎類型確實不是從要強制轉換為基礎類型的基礎派生的,則很有可能會遇到段錯誤。 如果您的類是多態的,則可以使用dynamic_cast安全地執行此操作,當您嘗試將派生的指針轉換為不是其派生類型的基本指針時,它將返回NULL

TestB *高級1 =&Basic; //這可行嗎?

不可以,因為向上投射不是一個好主意。

您應該使用多態,方法是在基礎中創建一個虛函數,然后在“派生”中覆蓋該函數。

如果要在派生類中使用變量A,則應對其進行保護,因為如果變量A是私有的,則不能通過公共繼承使用它。

我不會這樣做-正確使用繼承,即

TestA* Advanced1 = new TestB;
TestA* Advanced2 = new TestC;

// your previous code at this point is wrong - these functions should be virtual in base class and overridden in derived classes...
Advanced1->AdvancedFunction1(); // This now does what you want
Advanced2->AdvancedFunction2(); // and so does this...

分配很好...

您的方法的問題是,如果您具有虛函數(多重繼承),或者在某個時候您決定TestB需要成員等,將會發生什么。在許多級別上這都是錯誤的方法...

沒有。

首先,方法在您的示例中不是靜態的,因此您不能從類類型訪問它們。 您需要一個對象的實例。

現在假設您的意思是:

Advanced1->AdvancedFunction1(); 
Advanced2->AdvancedFunction2();

TestB公開擴展了TestA ,因此它具有所有功能以及更多功能。 所以TestB TestA

但是TestA 不是 TestB ,所以不能將TestA分配給TestB

因此,這里存在的問題很明顯, Advanced1Advanced2指向根本沒有該方法的Base類型的對象。 因此,您不能指向功能比聲明的變量少的東西。

上面的代碼調用了Undefined Behavior的守護進程。 它是形式上無效的代碼(前提是您添加了static_cast ,否則它將無法編譯),並且編譯器將樂於嘗試發明如何使貓變黑的方法。

Gcc以其創造力而著稱,它在懲罰敢於召喚野獸的程序員方面頗具創意。 在您的情況下,很有可能要么將相同的值常量傳播到兩個函數中,因為規范說Advanced1和Advanced2不能指向同一對象,或者優化了調用,因為規范說它們都不能指向相同的對象。盡管有種種跡象表明它們實際上確實是基本的。 相信我,如果確實是這樣的深一些廣告代碼,這是非常難以調試。

請注意,由於函數不是虛函數,因此除正式函數外,方法和(可能是重載的)自由函數之間絕對沒有區別。 因此,沒有什么可以阻止您簡單地寫:

void AdvancedFunction1(TestA &that) { that.A = that.A + that.A; }

然后:

TestA test;
AdvancedFunction1(test);

旁注:在您已經有了一些答案之后,完全重做問題是非常非常愚蠢的行為。 該問題的第一個版本具有Advanced1和Advanced2和Basic,而現在有了完全不同的名稱。

TestA Basic;
TestB *Advanced1 = &Basic; //Is this viable?
TestC *Advanced2 = &Basic; //And this?

不,孩子不能指向父親類型的對象。

TestB->AdvancedFunction1(); //Does this do what I think it does?
TestC->AdvancedFunction2(); //Or do implicit errors/problems occur?

它調用TestB :: AdvancedFunction1(也就是說,如果您已將&Basic強制(廣播)為Advanced1)。 結果可能是災難性的。

TestB AdvVar1;
TestC AdvVar2;

AdvVar1 = Basic; //Does this do what is intended? 
AdvVar2 = Basic; //Or do implicit errors occur?

一點也不。 盡管有時向下轉換(將父親投射到孩子)可能是合理的,但是復制(除非定義了operator =)是行不通的。

如果要列出所有帶有指向父親的指針的孩子,應該執行的操作是:

class TestA
{
public:
    enum _type
    {
         IS_TESTA = 0,
         IS_TESTB,
         IS_TESTC
    } type;
    /* other variables */
    TestA() {type = IS_TESTA;}
    virtual void common_function() { /* do something with TestA data */ }
    void father_function() {}
}

class TestB : public TestA
{
public:
    /* variables */
    TestB() {type = TestA::IS_TESTB;}
    void common_function() { /* do something with TestA & TestB data */ }
    void TestB_specific_function() {}
}

class TestC : public TestA
{
public:
    /* variables */
    TestC() {type = TestA::IS_TESTC;}
    void common_function() { /* do something with TestA & TestC data */ }
    void TestC_specific_function() {}
}

要創建列表,請執行以下操作:

TestA **list = new TestA *[size];
for (int i = 0; i < size; ++i)
    if (for_any_reason_create_TestB)
        list[i] = new TestB;
    else if (for_any_reason_create_TestC)
        list[i] = new TestC;
    else
        list[i] = new TestA;

現在,當您要使用變量時,當您要調用所有人共享的函數時,即使每個子類的實現方式都不同,您也可以這樣做:

for (int i = 0; i < size; ++i)
    list[i]->common_function();

然后它將根據實際對象類型自動調用TestA::common_functionTestB::common_functionTestC::common_function

如果要調用特定的子功能,可以執行此操作(但不建議這樣做):

for (int i = 0; i < size; ++i)
    if (list[i]->type == TestA::IS_TESTB)
        ((TestB *)list[i])->TestB_specific_function();
    else if (list[i]->type == TestA::IS_TESTC)
        ((TestC *)list[i])->TestC_specific_function();

活力

形容詞/ ˈvīəbəl /

  1. 能夠成功工作; 可行

如果可行,那是可行的。

在您的情況下,它可以工作。 但是,請考慮以下兩種情況:

-您可以添加虛擬功能。 那會導致它崩潰。

-您可能會更改AdvancedFunction1()以便對TestB類的成員進行操作,這將導致未定義的行為,因為您的對象不是TestB類型的,並且沒有TestB的成員。

編輯:

崩潰代碼:

class TestA
{
    public:
        int A;

        TestA(){A = 1;}
        const TestA &operator=(const TestA &Copy)
        {
            A = Copy.A;
        return *this;
        }
};

class TestB : public TestA
{
    public:
        using TestA::operator=;
        void AdvancedFunction1(){A = A + A;}
    virtual void f()
    {
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    TestA Test;
    TestB *Alt;
    Alt = (TestB *)&Test;
    Alt->AdvancedFunction1();
    Alt->f(); //crash here

    return 0;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM