C++子类模块化函数

发布于 2024-12-03 09:12:51 字数 799 浏览 3 评论 0原文

抱歉,鉴于代码中充满了我的错误并且没有传达我的观点,我决定从根本上更新问题。

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

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

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

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

    printf("%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'?).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

幽梦紫曦~ 2024-12-10 09:12:51

vi·a·ble

形容词/ˈ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;
}

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;
}
秉烛思 2024-12-10 09:12:51

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

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

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

挖鼻大婶 2024-12-10 09:12:51

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

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

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

您可以通过显式强制转换来完成此操作,但如果这样做,如果基础类型确实不是从您要强制转换的基类型派生的,则很可能会出现段错误。如果您的类是多态的,则可以使用dynamic_cast安全地执行此操作,当您尝试将派生指针强制转换为非类型的基指针时,该类会返回NULL源自。

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.

め可乐爱微笑 2024-12-10 09:12:51

不。

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

现在假设您的意思是:

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

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

但是 TestA 不是 TestB,因此您不能将 TestA 分配给 TestB< /代码>。

因此,问题在这里清楚地显示出来,Advanced1Advanced2指向Base类型的对象,而该对象根本没有该方法。因此,您不能指向功能比声明的变量类型少的东西。

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.

牵强ㄟ 2024-12-10 09:12:51

测试B *高级1 = &基本; //这可行吗?

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

您应该使用多态性,在基类中创建一个虚函数,然后在 Derived 中重写该函数。

如果要在派生类中使用变量 A,则应该将其设置为受保护的,因为如果它是私有的,则无法通过公共继承来使用它。

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.

留一抹残留的笑 2024-12-10 09:12:51
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?

一点也不。尽管有时向下转换(将父亲转换为孩子)可能是合理的,但复制(除非定义了运算符=)不起作用。

你应该做什么,如果你想要一个包含所有指向父亲的指针的孩子列表,你应该这样做:

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();
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();
笑忘罢 2024-12-10 09:12:51

上面的代码调用了Undefined Behaviour的守护进程。它是形式上无效的代码(假设您添加了static_cast,没有它就无法编译),并且编译器会很乐意发明如何将您的猫变成黑色的方法。

Gcc 尤其以其在惩罚敢于召唤野兽的程序员方面的创造力而闻名。在您的情况下,很可能会不断地将相同的值传播到两个函数中,因为规范说 Advanced1 和 Advanced2 不能指向同一个对象,或者优化调用,因为规范说两者都不能指向相同的对象尽管所有迹象表明它们实际上都是基本的。相信我,如果它在某些创造性代码的深处做了这样的事情,那么调试起来非常困难。

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

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

和比:

TestA test;
AdvancedFunction1(test);

旁注:当您已经有了一些答案时,完全重新设计问题是非常非常愚蠢的行为。问题的第一个版本有 Advanced1、Advanced2 和 Basic,现在它有完全不同的名称。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文