开发者

C++ subclasses modular functions

Sorry, I decided to radically update the question given the code was riddled with my errors and didn't convey my point.

I wanted to see whether or not the below is viable:

Here's an example that can be compiled:

#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();

    prin开发者_StackOverflow社区tf("%d!\n",Test.A);

    Test = Alt2;

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

    return 0;
}

Given, that TestB can (with casting) point to TestA, and can successfully modify TestA, is this a viable concept? It seems to work so, what are the pitfalls? (I know people say it's wrong but what is it that is specifically wrong with it aside from 'convention'?).


vi·a·ble

Adjective/ˈvīəbəl/

  1. Capable of working successfully; feasible

If it works, it's viable.

In your case, it works. However, consider these 2 scenariosn:

-You might add virtual functions. That will cause it to crash.

-You might change AdvancedFunction1() so that it operates on members of the class TestB, which will result in undefined behavior, because your object is not of the type TestB and does not have the members of a TestB.

EDIT:

Crash code:

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


I would not do this - use inheritance properly, i.e.

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...

The assignment is fine...

The problem with your approach is what happens if you have virtual functions (multiple inheritance), or at some point you decide that TestB needs members etc. It's a wrong approach on so many levels...


What you are doing is called upcasting. Upcasting is when you take a pointer to a derived type and try to assign it to a base type. It doesn't work.

You are only allowed to implicitly cast a pointer to a pointer of a type of one of its base classes. You can cast either a TestB* or a TestC* to a TestA*, but you cannot implicitly cast a TestB* to a TestC*, or a TestA* to either of its subclasses. So your example wouldn't even compile.

The reason is that, as in your example, if you could cast base pointers to derived pointers, you could call derived functions on base pointers, and that would cause many problems because you would try to access the derived's member variables and they wouldn't be there.

You can do this by an explicit cast, but if you do, you'll most likely get a segfault if the underlying type really isn't derived from the base type you're casting it to. You can do this safely with dynamic_cast if your class is polymorphic, which returns NULL when you try to cast a derived pointer to a base pointer of a type that it isn't derived from.


No.

First of all, the methods aren't static in your example so you can't access them from the class type. You need an instance of an object.

Now lets assume you meant:

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

TestB publicly extends TestA, so it has all its functionality and more. So TestB is a TestA.

But TestA is not a TestB, so you can't assign a TestA to a TestB.

So the problem with that is shown clearly here, Advanced1 and Advanced2 point at an object of type Base which does not have that method at all. So you can't point at something with less functionality than the type the variable is declared.


TestB *Advanced1 = &Basic; //Is this viable?

No, because upcasting is not a good idea.

You should use polymorphism, by creating a virtual function in your base and then overriding the function in Derived.

If you want to use your variable A in your derived classes, you should make it protected, because you cannot use it by public inheritance if it is private.


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

No, a child cannot point to an object of father type.

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

It calls TestB::AdvancedFunction1 (that is if you had forced (cast) &Basic to Advanced1). Results may be catastrophic.

TestB AdvVar1;
TestC AdvVar2;

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

Not at all. Although sometimes it may be reasonable to down-cast (cast a father to child), copying (unless operator = is defined) doesn't work.

What you should do, if you want to have a list of children all with pointers to father, here's what you should do:

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() {}
}

To create the list, you do this:

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;

Now, when you want to use the variables, when you want to call the function shared by all, even though each subclass has implemented it differently, you could do this:

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

And it would automatically call TestA::common_function, TestB::common_function or TestC::common_function depending on the real object type.

If you want to call specific children functions, you could do this (but is not recommended):

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();


The above code summons the daemon of Undefined Behaviour. It is formally invalid code (provided you add the static_cast without which it won't compile) and the compiler will be happy to invent way how to turn your cat black instead.

Gcc is especially renowned for it's creativity in punishing programmers who dare to summon the beast. In your case it would be quite likely to either constant-propagate the same values into both functions because specification says that Advanced1 and Advanced2 can't point to the same object or optimize the calls away because specification says neither can point to the same object as Basic despite all indications that they actually do. And believe me, if it does something like this deep in some creative code, it is very hard to debug.

Note, that since the functions are not virtual, there is absolutely no difference between methods and (possibly overloaded) free functions except formal ones. So there is nothing preventing you to simply write:

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

and than:

TestA test;
AdvancedFunction1(test);

On a side-note: It is very, very silly behaviour to completely rework the question when you already have some answers. The first version of the question had Advanced1 and Advanced2 and Basic and now it has altogether different names.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜