覆盖的几个基本问​​题

Dah*_*lia 2 c++ polymorphism overriding overloading

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

1:当我说base*b = new derived时,它是什么意思; 为什么一个人会这样做?我们很好地分别可以为类base和派生类创建对象,然后相应地调用函数.我知道这个基础*b =新派生的; 被称为对象切片,但为什么以及何时会这样做呢?

2:我知道为什么不建议将基类对象转换为派生类对象(因为基类不知道派生类成员和方法).我甚至在其他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;
    }
Run Code Online (Sandbox Code Playgroud)

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

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

In *_*ico 5

1.这不是对象切片:

base *b = new derived;
Run Code Online (Sandbox Code Playgroud)

这是将一个类型的指针分配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;
}
Run Code Online (Sandbox Code Playgroud)

这应该是预期的输出:

嗨,您好!

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

Observer类型定义了一个纯虚函数(=0意味着该函数是纯函数;它必须由派生类实现).该virtual关键字告诉编译器调用该函数应该导致执行最多派生的函数.该OnEvent()函数MyObserver是最来源的,因此,该版本被称为,即使我们呼吁OnEvent()对类型的指针Observer.

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

2.有没有错从基铸造一个指向派生类型本身.以下实际上是合法的,并保证有效:

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

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

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

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;
}
Run Code Online (Sandbox Code Playgroud)

d2p指针的值是没有意义的,并且尝试访问其中的任何内容肯定会导致崩溃,因为bp指针实际上并不指向Derived2实例,而是指向Derived1实例.编译器不能在编译的时候赶上这一点,因为这两个Derived1Derived2继承Base,所以投编译成功.这是从基类转换为派生类型的主要危险 - 如果转换实际上返回有意义的结果,您将不会知道直到运行时.

当然,除非您使用,否则dynamic_cast<>()演员会受到运行时惩罚.static_cast<>()最多涉及指针算术.reinterpret_cast<>()强制指针采用不同的(可能不相关的)类型,而不执行任何指针算法.这使得reinterpret_cast<>()其中一个更危险的演员表应该只在必要时使用,特别是如果static_cast<>()可以做到这一点.

3.考虑以下事项:

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;
}
Run Code Online (Sandbox Code Playgroud)

如果该Foobar()函数不是虚函数,那么您将获得此输出:

In Derived!
In Base!
Run Code Online (Sandbox Code Playgroud)

否则,如果该Foobar()函数是虚函数,那么您将获得此输出:

In Derived!
In Derived!
Run Code Online (Sandbox Code Playgroud)

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

// Prints "In Base!", even if bp actually points
// to an instance of Derived overriding Foobar().
bp->Base::Foobar();
Run Code Online (Sandbox Code Playgroud)