代码之家  ›  专栏  ›  技术社区  ›  bjskishore123

为什么在函数参数需要抽象类对象时使用传递值是危险的?

c++
  •  2
  • bjskishore123  · 技术社区  · 15 年前

    我在Addison Wesley常见问题中阅读了下面的声明。

    注意:按值传递对象可以 在某些情况下是危险的。经常 最好把物体传过去 引用const而不是传递它们 按价值计算。例如,传递值 如果目标类型为 在运行时的错误行为 参数的类已派生 上课。但是如果 参数不能有 呼叫需要本地副本 有用。

    如果目标类型是抽象类并且参数的类具有派生类,那么在运行时它是如何错误的行为的?

    编辑:那么,上面的陈述应该是 “编译时的错误行为”

    5 回复  |  直到 15 年前
        1
  •  6
  •   sellibitze    15 年前

    如果目标类型是抽象类并且参数的类具有派生类,那么在运行时它是如何错误的行为的?

        2
  •  7
  •   Community Mohan Dere    9 年前

    这里有两件事,你好像把它们搞混了:

    1) 如果目标类型是抽象类,并且通过值传递,则不会出现运行时错误:它不会编译,句点。

    2) 如果目标类型是具体的但具有派生类,则会出现运行时错误。举个例子:

    #include <iostream>
    
    struct A {
        virtual void f() { std::cout << "A\n"; }
    };
    struct B : public A {
        virtual void f() { std::cout << "B\n"; }
    };
    
    void func(A a){
        a.f();
    }
    
    int main() {
        func( B() );
    }
    

    它编译得很好,但是输出 A . 如果将参数更改为 A& B 是印刷品。这个问题叫做切片 nice question 就这样吧。

    实际上,我避免按值传递任何参数,除非我确定它是一个基元类型,如 int . 如果你在写一个模板,你最好接受所有的参数 const T& .

        3
  •  3
  •   liaK    15 年前

    它甚至不会编译。。

    来自标准文档 10.4.3条 ,

    抽象类 可以声明对抽象类的指针和引用。

        4
  •  1
  •   BenMorel Manish Pradhan    12 年前

    好吧,假设我们有以下层次结构

    #include <iostream>
    class Base
    {
    public:
       Base(): BaseNum(42)
       {}
       virtual void f()
       {
          std::cout << BaseNum;
       }
       virtual ~Base() 
       {
       }
    protected:
       int BaseNum;
    };
    
    class Derived: public Base
    {
    public:
       Derived(): DerivedNum(14)
       {}
       virtual void f()
       {
          std::cout << DerivedNum + BaseNum;
       }
    private:
       int DerivedNum;
    };
    

    如果有一个按值取基的函数,而传递的是Derived,则派生对象首先转换为Base(通过截断任何特定的fo派生信息,如DerivedNum),然后复制到参数中。因此,在函数中,您将丢失特定于派生的任何信息。这就是所谓的切片。在函数内部,如果调用了virtual f()函数,则无论传递的是派生的,都将调用Base::f。 如果通过引用或指针传递,则不会进行复制。从派生类型转换为基类型将保留动态类型信息,因为没有实际的复制,因此将执行切片。当然,不能将基用作派生,但如果调用f,则会调用派生::f。 哦。

        5
  •  0
  •   Helmut Grohne    15 年前

    我怀疑按值传递派生类实例是否可能。毕竟,在编译时必须知道传递值的大小。然而,抽象基类和派生类的大小可能(而且很可能)不同。由此产生的行为几乎没有定义。