2011-09-07 66 views
0

对不起,我决定从根本上更新这个问题,因为代码充斥着我的错误,并没有传达我的观点。C++子类模块化功能

我想看看是否不低于是可行的:

下面是可以被编译的例子:

#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,这是一个可行的概念吗?它似乎工作如此,有什么陷阱? (我知道人们说这是错误的,但是什么是,特别是与'convention'不符?)。

回答

1

六·一·竹叶提取

形容词/vīəbəl/

  1. 能够成功工作;可行

如果它有效,它是可行的。

在你的情况下,它的工作原理。但是,请考虑以下2个场景:

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

- 您可能会更改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; 
} 
+0

这是我正在寻找的答案。有人突出了特定技术的特定漏洞。通过向TestA添加虚拟功能来测试它不会导致崩溃。 – SSight3

+1

你试过打电话吗? –

+0

是的。唯一的缺点(取决于视图)是它被称为base的函数而不是TestB的,但是将它设置为非虚拟解决。也就是说,即使TestA和TestB都具有虚拟性,我称之为。我会尝试混合虚拟和非虚拟。 – SSight3

1

你在做什么叫做上溯造型。向上转换是指将一个指针指向派生类型并尝试将其分配给基类型。它不起作用。

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

原因是,就像在你的例子中一样,如果你可以将基指针转换为派生指针,你可以在基指针上调用派生函数,这会导致很多问题,因为你会尝试访问派生的成员变量,他们不会在那里。

您可以通过显式类型转换做到这一点,但如果你这样做,你很可能会获得一个段错误,如果基础类型还真不是从你强制转换为基本类型的。如果您的类是多态的,您可以安全地使用dynamic_cast来做到这一点,当您尝试将派生指针强制转换为非派生类型的基指针时,返回NULL

1

TestB * Advanced1 = &Basic; //这是否可行?

没有,因为向上转型是不是一个好主意。

你应该使用多态,通过建立在你的基地一个虚函数,然后在派生重写功能。

如果你想用你的变量A在你的派生类,你应该让受保护的,因为如果是私人无法通过公有继承使用它。

+2

指针不切。切片是在派生类的实例上使用基类的复制构造函数时。 –

+0

对不起。我希望它被保护而不是私人的。习惯的力量。感谢您的更正。 – SSight3

+0

是否有任何特定的原因向上播放不是一个好主意? – SSight3

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需要成员等,这是在很多层面上一种错误的做法...

+0

谢谢你的输入。他们只修改自己的副本。我在想更多的是一个集中的基类,更高级的功能可以修改然后返回。 – SSight3

1

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

现在让我们假设你的意思是:

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

TestB公开延伸TestA,所以它有它的所有功能和更多。所以TestBTestA

TestA不是TestB,所以你不能指定一个TestATestB

因此,与该问题清楚地示出这里,Advanced1和在Base类型的对象Advanced2点不具有该方法在所有。所以你不能指向比变量被声明的类型更少的功能。

+0

是的,我的意思是。大脑不在装备。纠正。为什么你不能指出它,考虑到TestB提供的功能? – SSight3

+0

因为在编译器中设置的类型只是为了类型安全而存在(很多脚本语言例如不需要类型安全)。所以虽然变量是一个指向该类型的指针,但实际的对象不是。在你的情况下,你清楚地创建了一个没有该方法的基类的对象。 –

1

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

海湾合作委员会在惩罚那些敢于召唤野兽的程序员方面尤其出名。在你的情况下,它很可能会不断传播相同的值到两个函数中,因为规范说Advanced1和Advanced2不能指向相同的对象或优化调用,因为规范说既不能指向相同的对象尽管所有迹象表明他们实际上都是基本的。相信我,如果它在某些创意代码中做了这样的深层次的事情,那么很难调试

请注意,由于函数不是虚拟的,除了正式函数之外,方法和(可能超载)的自由函数之间绝对没有区别。因此,有没有办法来防止你简单地写:

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

比:

TestA test; 
AdvancedFunction1(test); 

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

+0

但是当然这种行为是在理论上定义的? TestB/TestC不比TestA大,它们使用相同的变量,它只是扩展的函数。如果TestB/TestC有额外的变量,我可以看到一个问题,但他们不会有明显的原因。 – SSight3

+1

@ SSight3:未定义行为的可怕守护进程会在您做某些事情时被召唤,但圣经规范所说的是未定义的。 Ergo它*理论上未定义*(它可能很好地在特定实现中“实际定义”)。最危险的情况是当你做某件事时,这是正式的未定义的,但是你在你的生活中看不到如何编译器可以做任何比TheObviousThing™,因为GCC每天证明每一个未定义的行为有一个聪明的优化依赖于这个限制。 –

+0

但是,这不正是变量参数如何在最低级别的vprintf中工作?来自函数参数的指针,解释性地重写为别的东西?这不是如何将类转换为二进制逐字节(char)数据?从某种意义上说,我甚至不修改核心基类,只是函数,这在理论上与外部化相同。 – SSight3

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(也就是说,如果你强迫(CAST)&基本以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_function,TestB::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(); 
+0

谢谢你的深入解答。 – SSight3