Xin lỗi trước cho những gì có thể là một bài đăng đầu tiên ngớ ngẩn trên mặt đất vững chắc. Trong khi có rất nhiều tài liệu về chủ đề, rất ít trong số đó là dứt khoát và/hoặc dễ hiểu đối với tôi.C++: Nghiêm ngặt răng cưa và lạm dụng công đoàn
Tôi có lớp mẫu AlignedArray
để tự động cấp phát bộ nhớ trên heap với căn chỉnh tùy ý (tôi cần căn chỉnh 32 byte cho các thói quen lắp ráp AVX). Điều này đòi hỏi một số thao tác con trỏ xấu xí.
Agner Fog cung cấp một lớp mẫu trong cppexamples.zip lạm dụng liên minh để làm như vậy (http://www.agner.org/optimize/optimization_manuals.zip). Tuy nhiên, tôi biết rằng viết cho một thành viên của một công đoàn và sau đó đọc từ một kết quả khác trong UB.
AFAICT là an toàn để đặt biệt hiệu bất kỳ loại con trỏ nào thành char *
, nhưng chỉ theo một hướng. Đây là nơi hiểu biết của tôi bị mờ. Dưới đây là một phiên bản rút gọn của AlignedArray
lớp học của tôi (thực chất là một viết lại của Agner, để giúp hiểu biết của tôi):
template <typename T, size_t alignment = 32>
class AlignedArray
{
size_t m_size;
char * m_unaligned;
T * m_aligned;
public:
AlignedArray (size_t const size)
: m_size(0)
, m_unaligned(0)
, m_aligned(0)
{
this->size(size);
}
~AlignedArray()
{
this->size(0);
}
T const & operator [] (size_t const i) const { return m_aligned[i]; }
T & operator [] (size_t const i) { return m_aligned[i]; }
size_t const size() { return m_size; }
void size (size_t const size)
{
if (size > 0)
{
if (size != m_size)
{
char * unaligned = 0;
unaligned = new char [size * sizeof(T) + alignment - 1];
if (unaligned)
{
// Agner:
/*
union {
char * c;
T * t;
size_t s;
} aligned;
aligned.c = unaligned + alignment - 1;
aligned.s &= ~(alignment - 1);
*/
// Me:
T * aligned = reinterpret_cast<T *>((reinterpret_cast<size_t>(unaligned) + alignment - 1) & ~(alignment - 1));
if (m_unaligned)
{
// Agner:
//memcpy(aligned.c, m_aligned, std::min(size, m_size));
// Me:
memcpy(aligned, m_aligned, std::min(size, m_size));
delete [] m_unaligned;
}
m_size = size;
m_unaligned = unaligned;
// Agner:
//m_aligned = aligned.t;
// Me:
m_aligned = aligned;
}
return;
}
return;
}
if (m_unaligned)
{
delete [] m_unaligned;
m_size = 0;
m_unaligned = 0;
m_aligned = 0;
}
}
};
Vì vậy, phương pháp nào là an toàn (r)?
Thay vì xây dựng 'đối tượng char' và sau đó đúc kết đến T, tại sao bạn không lấy nguyên bộ nhớ (từ 'hành new' , hoặc thậm chí 'malloc'), như' void * ', và thực sự xây dựng các đối tượng' T' trong đó? Về cơ bản: nếu bạn muốn T đối tượng, xây dựng các đối tượng T. Trường hợp sử dụng này (mảng liên kết) có * zero * cần cho bí danh thủ thuật/công đoàn/memcpy/bất cứ điều gì. –
@ R.MartinhoFernandes: Ngoại trừ, toán học không được phép trên 'void *' s. Làm thế nào để bạn có được một liên kết 'void *'? – Omnifarious
@Omnifarious Cuối cùng tôi đã kiểm tra, toán học không được phép trên 'char *'. (Và ngay cả khi nó được, đó sẽ không có nghĩa là bạn cần phải xây dựng các đối tượng char và không xây dựng các đối tượng T) Bạn cần số nguyên để làm toán học. Giải pháp di động trong C++ 11 là http://en.cppreference.com/w/cpp/memory/align. Giải pháp lý thuyết không phải là di động là reinterpret_cast thành kiểu số, thực hiện phép toán và reinterpret_cast. (nó khá di động trong thực tế bởi vì trong tất cả các triển khai, tôi biết reinterpret_cast cho các kiểu số hoạt động như mong đợi) –