繁体   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