簡體   English   中英

理解對象切片

[英]Understanding object slicing

為了理解對象切片的問題,我想我已經創建了一個可怕的例子,我正在嘗試測試它。 然而,這個例子並沒有我想象的那么糟糕。

下面是一個最小的工作示例,如果您能幫助我理解為什么它仍然“正常工作”,我將不勝感激。 如果你幫我把例子弄得更糟,那就更好了。

#include <functional>
#include <iostream>

template <class T> class Base {
protected:
  std::function<T()> f; // inherited

public:
  Base() : f{[]() { return T{0}; }} {} // initialized
  virtual T func1() const { return f(); }
  virtual ~Base() = default; // avoid memory leak for children
};

template <class T> class Child : public Base<T> {
private:
  T val;

public:
  Child() : Child(T{0}) {}
  Child(const T &val) : Base<T>{}, val{val} { // initialize Base<T>::f
    Base<T>::f = [&]() { return this->val; }; // copy assign Base<T>::f
  }
  T func1() const override { return T{2} * Base<T>::f(); }
  void setval(const T &val) { this->val = val; }
};

template <class T> T indirect(const Base<T> b) { return b.func1(); }

int main(int argc, char *argv[]) {
  Base<double> b;
  Child<double> c{5};

  std::cout << "c.func1() (before): " << c.func1() << '\n'; // as expected
  c.setval(10);
  std::cout << "c.func1() (after): " << c.func1() << '\n'; // as expected

  std::cout << "indirect(b): " << indirect(b) << '\n'; // as expected
  std::cout << "indirect(c): " << indirect(c) << '\n'; // not as expected

  return 0;
}

我編譯代碼時得到的輸出如下:

c.func1() (before): 10
c.func1() (after): 20
indirect(b): 0
indirect(c): 10

我希望最后一行拋出一些異常或只是失敗。 c的基本部分被indirect切片時,在 lambda 表達式中沒有可使用的this->val (我知道,C++ 是一種靜態編譯語言,而不是動態語言)。 我也嘗試在復制分配Base<T>::f時按值捕獲this->val ,但它沒有改變結果。

基本上,我的問題有兩個方面。 首先,這是未定義的行為,還是僅僅是法律法規? 其次,如果這是一個合法的代碼,為什么切片不會影響行為? 我的意思是,我可以看到T func1() const是從Base<T>部分調用的,但是為什么捕獲的值不會造成任何麻煩?

最后,我如何構建一個具有更糟糕副作用的示例,例如內存訪問類型的問題?

提前感謝您的時間。

編輯。 我知道另一個主題已被標記為重復。 我已經閱讀了那里的所有帖子,事實上,我一直試圖復制那里的最后一個帖子 正如我在上面問的那樣,我正在嘗試獲得這種行為

那么b中關於會員欄的信息在a中丟失了。

我不能完全得到。 對我來說,似乎只丟失了部分信息。 基本上,在最后一個帖子中,該人聲稱

來自實例的額外信息已經丟失,並且 f 現在容易出現未定義的行為。

在我的示例中, f似乎也能正常工作。 相反,我只需要調用T Base<T>::func1() const ,這並不奇怪。

您當前的代碼沒有未定義的行為。 但是,它很危險,因此很容易產生未定義的行為。

切片發生了,但您可以訪問this->val 看起來很神奇,但你只是從你的主訪問this->val from Child<double> c

那是因為 lambda 捕獲。 您捕獲this ,它指向您的 main 中的c變量。 然后將該 lambda 分配到基類中的std::function 您的基類現在有一個指向c變量的指針,以及一種通過std::function訪問valstd::function

所以切片發生了,但您可以訪問未切片的對象。

這也是為什么數字不乘以二的原因。 虛擬調用解析為 base,並且 main 中cval的值為10

您的代碼大致相當於:

struct B;

struct A {
    B* b = nullptr;

    int func1() const;
};

struct B : A {
    int val;
    explicit B(int v) : A{this}, val{v} {}
};

int A::func1() const {
    return b->val;
}

int main() {
    B b{10};

    A a = b;

    std::cout << a.func1() << std::endl;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM