覆盖的几个基本问​​题

时间:2010-05-26 03:02:49

标签: c++ polymorphism overloading override

我的基本问题很少,如果有人能清楚这一点,我会感激不尽。

1 :当我说base * b时,这是什么意思     =新派生的;为什么一个人会这样做?我们很好地分开可以     为类库创建对象     类派生然后调用     相应的功能。我知道     这个基数* b =新派生的;是     称为对象切片,但为什么和     什么时候会这样做?

2 :我知道     为什么不建议转换     要派生的基类对象     class对象(因为基类是     不知道派生类     成员和方法)。我甚至读过     其他StackOverflow线程如果     这就是我们的情况     必须改变/重新访问我们的设计。     但我知道这一切     只是好奇,有什么办法可以做     此?

    class base
    {
    public:
        void f(){cout << "In Base";}
    };

    class derived:public base
    {
    public:
        void f(){cout << "In Derived";}
    };

    int _tmain(int argc, _TCHAR* argv[])
    {
        base b1, b2;
        derived d1, d2;
        b2 = d1;
        d2 = reinterpret_cast<derived*>(b1); //gives error C2440
        b1.f(); // Prints In Base
        d1.f(); // Prints In Derived
        b2.f(); // Prints In Base
        d1.base::f(); //Prints In Base
        d2.f();
        getch();
        return 0;
    }

3:如果是上面的例子,有没有办法可以使用派生类对象调用基类f()?我使用了d1.base():: f()我只想知道是否有任何方法不使用范围解析运算符?

非常感谢您抽出时间帮助我!

5 个答案:

答案 0 :(得分:5)

1。这不是对象切片:

base *b = new derived;

这是将base类型的指针分配给derived的实例。

其中一个(非常常见)的例子就是回调。考虑一下:

class Observer
{
public:
    virtual void OnEvent() = 0;
};

class Subject
{
public:
    Subject() : observer(0) {}
    void SetObserver(Observer* o) { observer = o; }
    void FireEvent() { if(observer != 0) observer->OnEvent(); }
private:
    Observer* observer;
};

class MyObserver : public Observer
{
public:
    void OnEvent() { ::printf("Hi there!\n"); }
};

int main()
{
    Subject s;
    MyObserver o;
    s.SetObserver(&o);
    s.FireEvent();
    return 0;
}

这应该是预期的输出:

  

你好!

注意这里发生了什么。我们传入指向MyObserverSetObserver()的实例的指针,即使该函数只接受Observer类型的指针。这是有效的,因为MyObserver(公开)来自Observer。在这种情况下,我们说MyObserver 是一个 Observer

Observer类型定义纯虚函数=0表示函数是纯函数;它必须由派生类实现)。 virtual关键字告诉编译器调用该函数应该导致执行派生最多的函数。 OnEvent()中的MyObserver函数是派生最多的函数,因此,即使我们在OnEvent()类型的指针上调用Observer,也会调用该版本。

我们经历了这样做的麻烦,因为在这段代码中Subject不必知道其观察者的确切类型 - 观察者只需从Observer和{{{{}}派生出来1}}实例将调用最派生类型的Subject实现。这允许代码解耦 - OnEvent()不依赖于Subject,而MyObserver不依赖于MyObserver

2。将指针从基础转换为派生类型本身并没有错。以下实际上是合法的,并保证可行:

Subject

然而,此片段中return语句之前的行未定义:

class Base {};
class Derived : public Base {};

int main()
{
    Derived d;
    Base* bp = &d;
    Derived* dp = static_cast<Derived*>(bp);
    return 0;
}

class Base {}; class Derived1 : public Base {}; class Derived2 : public Base {}; int main() { Derived1 d1; Base* bp = &d1; Derived2* d2p = static_cast<Derived2*>(bp); // WTF?! return 0; } 指针的值没有意义,尝试访问其中的任何内容肯定会导致崩溃,因为d2p指针实际上并不指向bp实例,它指向Derived2实例。编译器无法在编译时捕获它,因为Derived1Derived1都继承自Derived2,因此转换成功编译。这是从基类转换为派生类型的主要危险 - 如果转换实际上返回有意义的结果,您将无法知道直到运行时。

当然,除非你使用Base,否则演员会受到运行时惩罚。 dynamic_cast<>()最多涉及指针算术。 static_cast<>()强制指针采用不同的(可能不相关的)类型,而不执行任何指针算法。这使得reinterpret_cast<>()成为更危险的演员之一,并且只在必要时才使用,特别是如果reinterpret_cast<>()可以完成这项工作。

3。请考虑以下事项:

static_cast<>()

如果class Base { public: void Foobar() { ::printf("In Base!\n"); } }; class Derived : public Base { public: void Foobar() { ::printf("In Derived!\n"); } }; int main() { Derived d; Derived* dp = &d; Base* bp = dp; dp->Foobar(); bp->Foobar(); return 0; } 功能不是虚拟的,那么您将获得此输出:

Foobar()

否则,如果In Derived! In Base! 函数是虚拟的,那么您将获得此输出:

Foobar()

为了保证对虚函数In Derived! In Derived! 的调用通过基指针调用基本实现,那么你必须使用范围解析运算符:

Foobar()

答案 1 :(得分:0)

是和否。你可以走了 class derived: public base { public: void f() { base::f(); } };

我不太确定全球范围。

答案 2 :(得分:0)

问题1:你为什么要这样做:你可能不会直接这样做。发生这种情况的情况将是你所拥有的,例如,一个函数返回“某种基础*”而你不关心哪种类型。

问题2:从派生内部,您需要使用范围解析运算符来指定您要使用基类的实现。

注意:您尚未将您的函数声明为虚拟,因此您实际上并没有获得您期望的行为。

答案 3 :(得分:0)

base *b = new derived

创建指向派生类对象的基类指针。这允许您创建 is-a 关系。类派生基础。有关详细信息,请参阅inhertance

2 . d2 = reinterpret_cast<derived*>(b1);  您正在尝试将基类对象转换为派生类对象。但使用的演员操作是。它不正确。

如果您打算调用基类方法,则使用Base类指针。

Base* ptr = &b1;
ptr->f();

我建议您参考FAQ Lite

答案 4 :(得分:0)

base *b = new derived;

(1)您有一个基类指针指向派生类对象。这在实现多态时使用,其中有几个派生类继承自公共基类。然后,如果函数是虚函数,则可以通过基类指针根据类型调用正确的派生类函数。


class base
{
    public:
        virtual void f(){ cout << "In Base" << endl; }
};

class derived:public base
{
    public:
        void f(){ cout << "In Derived" << endl; }
};

class derived2: public derived
{
public:
    void f() { cout << "In Derived2" << endl; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    derived d1;
    derived2 d2;
    base* b;
    b = &d1;
    b->f();   // calls derived::f();
    b = &d2;
    b->f();   // calls derived2::f();
    return 0;
}

它不称为对象切片。对象切片是指将派生类对象分配给基类对象(而不是指针),其中派生类对象的成员被切片,保持不变。因此,您无法从基类对象访问这些切片成员。

(2)除了使用reinterpret_cast的行之外,其他语句都没有问题。

(3)如果您正在使用对象或覆盖派生类函数来调用基类版本(如其他答案中所述),则必须使用范围解析运算符。但是,您可以使用指针:

derived *d = static_cast( &b1 );
d->f();