2014-01-21 27 views
1

我得到了意想不到的结果,至今我都无法解释自己。C++协方差意外行为

虽然生成了两次同一类BASIC2(继承自BASIC1)的对象,但在生成的对象上使用好友operator<<时会生成不同的消息。下面

查看代码,我让D(一类DerivedBase继承的对象)产生BASIC2类的一个对象,并调用它的朋友operator<<。这产生了预期的消息"BASIC2 object"

我让B生成类BASIC1的一个对象,并将其称为其朋友operator<<。如我所料,这输出"BASIC1 object"

然后我用虚拟继承让B2Base* B2 = &D;)生成一个对象BASIC2。我遵循调试器(Visual Studio 2010)中的代码流,这会正确生成BASIC2的对象。但是,朋友operator<<未在BASIC2对象上调用,但使用类BASIC1的朋友operator<<(因此输出"BASIC1 object")。

另请注意,我希望BASIC2继承自BASIC1,因为我想利用协变性行为。

int main(int argc, char* argv[]) { 
    Base B; 
    Derived D; 
    Base* B2 = &D; 
    std::cout << *D.generate(0) << std::endl; 
    std::cout << *B.generate(0) << std::endl; 
    std::cout << *(B2->generate(0)) << std::endl; 
    system("pause"); 
} 

输出是:

 
BASIC2 object 
BASIC1 object 
BASIC1 object 
class BASIC1 { 
public: 
    friend std::ostream& operator<<(std::ostream& os, const BASIC1& basic) { 
    os << "BASIC1 object"; 
    return os; 
    } 
}; 

class BASIC2 : public BASIC1 { 
    friend std::ostream& operator<<(std::ostream& os, const BASIC2& basic) { 
    os << "BASIC2 object"; 
    return os; 
    } 
}; 

class Base { 
public: 
    virtual BASIC1* generate(double num) const { 
    return new BASIC1(); 
    } 
protected: 
private: 
}; 

class Derived : public Base { 
public: 
    virtual BASIC2* generate(double num) const override { 
    return new BASIC2(); 
    } 
protected: 
private: 
}; 
+0

那么'B2'的静态类型是'Base',所以编译器看到的函数是'BASIC1 * Base :: generate(double)'(动态调度调用'Derived :: generate')。不过,返回类型是'BASIC1 *'。并且基于表达式*(B2-> generate(0))'的静态类型选择'operator <<'。 – dyp

+1

@dyp:I.e.这个'operator <<'不是''virtual'“。 –

+1

运行时/动态分派只在成员函数中完成。 – molbdnilo

回答

5

操作< <的选择是基于静态类型在编译时知道您的对象。为了实现你想要不定义两个运营商< <功能是什么,但一个只在BASIC1:

friend std::ostream& operator<<(std::ostream& os, const BASIC1& basic) { 
     Write(os); 
     return os; 
    } 

,并在这两个BASIC1定义虚拟函数编写和BASIC2做你想做什么:

virtual void Write(std::ostream& os) const { 
    os << "BASIC1 object"; 
} 
+0

应该使用:virtual void Write(std :: ostream&os)const {“BASIC1 object”; },因为我传递一个const Basic&object – user3116431

+0

是的,最好是尽可能强制执行const。 –

+0

写入必须被赋予一个const方法限定符,否则(它不会被编译),你必须保证你传递给operator <<的const对象不会被修改。 – user3116431

1

谢谢!对我来说,我的问题的最终结论是:如果您通过动态调度生成对象的对象,请给朋友操作员打电话<(std :: ostream & os,const Base &对象)。您需要在运营商< <中使用虚拟功能,以允许动态调度运营商< <。