当前位置: 首页 > 知识库问答 >
问题:

什么是对象切片?

翟昊明
2023-03-14

有人在IRC中提到它是切片问题。

共有2个答案

公西培
2023-03-14

这里的大多数答案都无法解释切片的实际问题是什么。他们只解释切片的良性案例,而不解释奸诈的案例。与其他答案一样,假设您处理的是两个类AB,其中B(公开)来源于A

在这种情况下,C++允许将b的实例传递给a的赋值运算符(以及复制构造函数)。这是因为b的实例可以转换为常量a&,这是赋值运算符和复制构造函数希望它们的参数是什么。

B b;
A a = b;

那里没有什么不好的事情发生--您要求a的一个实例,它是B的副本,而这正是您得到的。当然,A不会包含B的某些成员,但它应该如何包含?这是一个a,毕竟不是B,所以它甚至没有听说过这些成员,更不用说能够存储它们了。

B b1;
B b2;
A& a_ref = b2;
a_ref = b1;
//b2 now contains a mixture of b1 and b2!

您可能认为b2将是b1的副本。但是,唉,不是!如果您检查它,就会发现b2是弗兰肯斯坦式的生物,由b1的一些块(ba继承的块)和b2的一些块(只有b包含的块)组成。哎哟!

怎么了?当然,C++默认情况下不会将赋值运算符视为virtual。因此,行a_ref=b1将调用a的赋值运算符,而不是b的赋值运算符。这是因为,对于非虚函数,声明的(形式上:静态)类型(即a&)决定调用哪个函数,而实际的(形式上:动态)类型(即b,因为a_ref引用了b的实例)则相反。现在,A的赋值运算符显然只知道A中声明的成员,因此它将只复制那些成员,而不改变B中添加的成员。

只给对象的部分赋值通常没有什么意义,但不幸的是,C++没有提供禁止这样做的内置方法。不过,你可以自己滚。第一步是使赋值运算符虚拟化。这将保证调用的始终是实际类型的赋值运算符,而不是声明类型的赋值运算符。第二步是使用dynamic_cast验证分配的对象是否具有兼容的类型。第三步是在一个(protected!)中进行实际赋值成员assign(),因为bassign()可能希望使用aassign()复制a的成员。

class A {
public:
  virtual A& operator= (const A& a) {
    assign(a);
    return *this;
  }

protected:
  void assign(const A& a) {
    // copy members of A from a to this
  }
};

class B : public A {
public:
  virtual B& operator= (const A& a) {
    if (const B* b = dynamic_cast<const B*>(&a))
      assign(*b);
    else
      throw bad_assignment();
    return *this;
  }

protected:
  void assign(const B& b) {
    A::assign(b); // Let A's assign() copy members of A from b to this
    // copy members of B from b to this
  }
};

注意,为了方便起见,boperator=协变重写了返回类型,因为它知道返回的是b实例

颜博达
2023-03-14

“切片”是指将派生类的对象分配给基类的实例,从而丢失部分信息--其中一些信息被“切片”掉了。

例如,

class A {
   int foo;
};

class B : public A {
   int bar;
};

因此b类型的对象有两个数据成员,foobar

如果你要这样写:

B b;

A a = b;

b中关于成员bar的信息在a中丢失。

 类似资料:
  • 我正在尝试对函数返回的值发出警报,并且在警报中得到以下信息: 下面是JavaScript代码: 是我试图检查的函数。

  • 问题内容: 运行python脚本时出现此错误: 我很确定’str’是字符串,但是我不知道’NoneType’对象是什么。我的脚本在第二行开始,我知道第一个行之有效,因为该行的命令符合我的期望。起初我以为可能是因为我在send_command中使用了变量和用户输入。 “ CAPS”中的所有内容都是变量,“小写”中的所有内容都是从“ parser.add_option”选项输入的。 我正在使用pexp

  • 问题内容: 任何人都可以确认我是否正确地在下面的方法调用中看到了该参数: 作为对象类型的数组?我不记得以前在Java中见过。 问题答案: 它等效于,但允许调用者一次只指定一个值作为参数,编译器将创建一个数组。所以这个电话: 相当于 有关更多信息,请参见varargs功能的文档(在Java 5中引入)。

  • 问题内容: 你能给我一些有关对象头中确切存储的信息吗?我知道,这可能取决于JVM,但也许至少对于HotSpot?我正在寻找专门针对第一行的确切描述。 我已经阅读了一些信息,这些信息无法用我找到的信息进行正面验证。也许你有指向全部内容的OpenJDK Wiki的链接? 问题答案: 对象头由一个标记词和一个klass指针组成。 所述标记字具有(字大小在32位体系结构中,在64位体系结构)和 在克拉斯指

  • 问题内容: Swift中的切片是什么,它与数组有何不同? 从文档中,下标(Range)的类型签名为: 为什么不返回另一个而不是一个? 看起来我可以将切片与数组连接起来: 但这会产生错误: 无法找到接受提供的参数的“下标”的重载 什么是切片? 问题答案: 切片指向数组。当数组已经存在并且切片只能描述其所需部分时,再也没有必要制作另一个数组。 加法会导致隐式强制,因此可以正常工作。为了使您的作业正常进

  • Swift中的切片是什么?它与数组有何不同? 从文档中可以看出,下标(范围)的类型签名是: 为什么不返回另一个而不是? 看起来我可以将一个片与一个数组连接起来: 但这会产生错误: 找不到接受所提供参数的“subscript”的重载 什么是切片?