C ++子类模块化函数

时间:2011-09-07 12:26:12

标签: c++ subclass

抱歉,我决定彻底更新问题,因为代码中包含了我的错误并且没有传达我的观点。

我想知道下面的内容是否可行:

这是一个可以编译的例子:

#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,这是一个可行的概念吗?它似乎有效,有哪些陷阱? (我知道人们说这是错的,但是除了“惯例”之外,具体错误的是什么?)。

7 个答案:

答案 0 :(得分:1)

您正在做的事情称为 upcasting 。向上转换是指获取派生类型的指针并尝试将其指定给基类型。它不起作用。

只允许隐式地将指针强制转换为其某个基类类型的指针。您可以将TestB*TestC*投射到TestA*,但不能隐含地将TestB*投射到TestC*TestA*到它的任何一个子类。所以你的例子甚至都不会编译。

原因在于,如在您的示例中,如果您可以将基本指针强制转换为派生指针,则可以在基本指针上调用派生函数,这会导致许多问题,因为您将尝试访问派生的成员变量并且它们不会在那里。

您可以通过显式转换来执行此操作,但如果您这样做,如果基础类型实际上不是从您将其转换为基类型派生的,那么您很可能会遇到段错误。如果您的类是多态的,则可以使用dynamic_cast安全地执行此操作,当您尝试将派生指针强制转换为不是派生类型的基类指针时,会返回NULL

答案 1 :(得分:1)

  

TestB * Advanced1 =&amp; Basic; //这可行吗?

不,因为向上转换不是一个好主意。

你应该使用多态,在你的基础中创建一个虚函数,然后覆盖Derived中的函数。

如果要在派生类中使用变量A,则应使其受保护,因为如果它是私有的,则不能通过公共继承使用它。

答案 2 :(得分:1)

我不这样做 - 正确使用继承,即

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需要成员等等。这在很多层面都是错误的方法......

答案 3 :(得分:1)

没有

首先,这些方法在您的示例中不是静态的,因此您无法从类类型中访问它们。你需要一个对象的实例。

现在假设你的意思是:

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

TestB公开扩展TestA,因此它具有所有功能和更多功能。因此TestB TestA

TestA 不是 TestB,因此您无法将TestA分配给TestB

所以问题就在这里清楚地显示了, Advanced1 Advanced2 指向Base类型的对象,它根本没有该方法。所以你不能指出功能少于声明变量类型的东西。

答案 4 :(得分:1)

上面的代码召唤了未定义行为的守护进程。它是正式无效的代码(如果您添加static_cast没有它将无法编译),编译器将很乐意发明如何将您的猫变黑。

Gcc以其在惩罚敢于召唤野兽的程序员方面的创造力而闻名。在您的情况下,很可能将相同的值持续传播到两个函数中,因为规范说Advanced1和Advanced2不能指向同一个对象或优化调用,因为规范说两者都不能指向同一个对象虽然有迹象表明他们确实做到了,相信我,如果它在某些创意代码中做了类似的事情,那么非常难以调试。

注意,由于函数不是虚函数,因此除了正式函数之外,方法和(可能超载)自由函数之间绝对没有区别。所以没有什么可以阻止你简单地写:

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

而不是:

TestA test;
AdvancedFunction1(test);

侧面说明:当你已经有了一些答案时,完全重写这个问题是非常非常愚蠢的行为。问题的第一个版本有Advanced1和Advanced2以及Basic,现在它有完全不同的名称。

答案 5 :(得分:1)

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(即如果你强制(​​强制转换)和基本到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();

答案 6 :(得分:1)

VI·A·BLE

形容词/vīəbəl/

  1. 能够成功运作;可行
  2. 如果有效,那就可行了。

    在你的情况下,它有效。但是,请考虑以下两种情况:

    - 您可以添加虚拟功能。这将导致它崩溃。

    - 您可能会更改AdvancedFunction1(),以便它对类TestB的成员进行操作,这会导致未定义的行为,因为您的对象不属于TestB类型,并且没有TestB的成员。 / p>

    编辑:

    崩溃代码:

    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;
    }