[英]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
訪問val
的std::function
。
所以切片發生了,但您可以訪問未切片的對象。
這也是為什么數字不乘以二的原因。 虛擬調用解析為 base,並且 main 中c
中val
的值為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.