鑒于此代碼:
#include <cassert>
#include <cstring>
struct base{
virtual ~base() = default;
};
class derived: public base{
public:
int x;
};
using byte = unsigned char;
int main() {
byte data[sizeof(derived)];
derived d;
memcpy(data, &d, sizeof(derived));
base* p = static_cast<base*>(reinterpret_cast<derived*>(data));
const auto offset = (long)data - (long)p;
assert(offset < sizeof(derived)); // <-- Is this defined?
}
正如我的評論所問,這是標準定義的行為嗎?即轉換到基保證一個指向被轉換的派生占用的區域的指標嗎?從我的測驗來看,它適用于 gcc 和 clang,但我想知道它是否也適用于跨平臺(顯然這個版本假設 64 位指標)
uj5u.com熱心網友回復:
從強制轉換到基類的指標是否是指向派生物件記憶體區域的指標
一般情況下不一定。如果基是虛擬的,并且所討論的派生物件不是最派生的物件,那么在這種情況下,虛擬基可能在派生物件的記憶體之外。
但是在這種極端情況之外,例如在示例代碼中,基礎子物件確實保證在派生物件內。這就是“子物件”的含義。
uj5u.com熱心網友回復:
可能錯誤對齊
你的data陣列是一個字符陣列,所以它的對齊方式是 1 個位元組。
但是int,您的類包含一個成員,因此其對齊方式至少為 4 個位元組。
所以你的data陣列不夠對齊,甚至無法包含派生物件。
您可以通過提供至少為derived或更大的資料陣列的對齊方式來輕松解決此問題,例如:
alignas(alignof(derived)) byte data[sizeof(derived)];
(godbolt 演示了這個問題)
std::aligned_storage如果你愿意,你也可以使用它。
在類上使用 memcpy 并不總是安全的
memcpy在類上使用只有在它們可以簡單地復制時才有效(因此按位元組復制與呼叫復制建構式相同)。由于虛擬解構式,您的類不可memcpy復制,因此不允許復制該類。
您可以使用以下方法輕松檢查std::is_trivially_copyable_v:
std::cout << std::is_trivially_copyable_v<derived> << std::endl;
您可以通過呼叫復制建構式而不是使用來輕松解決此問題memcpy:
alignas(alignof(derived)) char data[sizeof(derived)];
derived d;
derived* derivedInData = new (data) derived(d);
虛擬繼承、多重繼承和其他惡作劇
類在記憶體中的布局方式是實作定義的,因此您基本上無法保證編譯器將如何布局您的類層次結構。
但是,您可以依靠以下幾點:
sizeof(cls)將始終回傳大小cls需求,包括它的所有基類,即使它使用虛擬和/或多重繼承。(大小)當應用于型別別時,結果是該類的完整物件占用的位元組數,包括將此類物件放入陣列所需的任何額外填充。
- 放置 new 將構造一個物件并回傳一個指向它的指標,該指標位于給定的緩沖區內。
static_cast<>基類總是被定義
實際答案
是的,基類指標必須始終指向緩沖區內的某個位置,因為它是派生類的一部分。但是,它在緩沖區中的確切位置是實作定義的,因此您不應依賴它。
從放置 new 回傳的指標也是如此——它可能指向陣列的開頭或其他地方(例如陣列分配),但它總是在資料陣列內。
因此,只要您堅持其中一種模式:
struct base { int i; }
struct derived : base { int j; };
alignas(alignof(derived)) char data[sizeof(derived)];
derived d;
memcpy(data, &d, sizeof(derived)); // trivially copyable
derived* ptrD = reinterpret_cast<derived*>(data);
base* ptrB = static_cast<base*>(ptrD);
/
struct base { int i; virtual ~base() = default; }
struct derived : base { int j; };
alignas(alignof(derived)) char data[sizeof(derived)];
derived d;
derived* ptrD = new(data) derived(d); // not trivially copyable
base* ptrB = static_cast<base*>(ptrD);
ptrD->~derived(); // remember to call destructor
您的斷言應該成立并且代碼應該是可移植的。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/366067.html
