Tôi đã thử nghiệm một số mã nơi có một thành viên dữ liệu std::vector
bên trong một lớp học. Lớp vừa copiable và di chuyển, và operator=
được thực hiện như mô tả here sử dụng sao chép và hoán đổi thành ngữ.Tính không hiệu quả của thành ngữ sao chép và hoán đổi?
Nếu có hai vector
s, nói v1
với công suất lớn và v2
với công suất nhỏ, và v2
được sao chép vào v1
(v1 = v2
), công suất lớn trong v1
được giữ sau khi chuyển nhượng; điều này có ý nghĩa, vì các cuộc gọi v1.push_back()
tiếp theo không cần phải buộc các đường dẫn mới (nói cách khác: giải phóng bộ nhớ đã có sẵn, sau đó phân bổ lại để phát triển vectơ không có ý nghĩa nhiều).
Nhưng, nếu việc chuyển nhượng tương tự được thực hiện với lớp có vector
như thành viên dữ liệu, hành vi này là khác nhau, và sau khi chuyển nhượng công suất lớn hơn là không giữ.
Nếu thành ngữ sao chép và trao đổi là không sử dụng, và sao chép và di chuyển operator=
operator=
được thực hiện riêng, sau đó hành vi được như mong đợi (như đối với bình thường không thành viên vector
s).
Tại sao lại như vậy? Chúng tôi có nên không theo thành ngữ sao chép và hoán đổi và thay vào đó triển khai operator=(const X& other)
(sao chépop=
) và operator=(X&& other)
(di chuyểnop=
) riêng biệt để có hiệu suất tối ưu?
Đây là sản phẩm của một thử nghiệm tái sản xuất với sao chép và hoán đổi thành ngữ (lưu ý như thế nào trong trường hợp này, sau khi x1 = x2
, x1.GetV().capacity()
là 1.000, không 1.000.000):
C:\TEMP\CppTests>cl /EHsc /W4 /nologo /DTEST_COPY_AND_SWAP test.cpp test.cpp C:\TEMP\CppTests>test.exe v1.capacity() = 1000000 v2.capacity() = 1000 After copy v1 = v2: v1.capacity() = 1000000 v2.capacity() = 1000 [Copy-and-swap] x1.GetV().capacity() = 1000000 x2.GetV().capacity() = 1000 After x1 = x2: x1.GetV().capacity() = 1000 x2.GetV().capacity() = 1000
Đây là đầu ra mà không có thành phần sao chép và hoán đổi (lưu ý cách thức trong trường hợp này x1.GetV().capacity() = 1000000
, như mong đợi):
C:\TEMP\CppTests>cl /EHsc /W4 /nologo test.cpp test.cpp C:\TEMP\CppTests>test.exe v1.capacity() = 1000000 v2.capacity() = 1000 After copy v1 = v2: v1.capacity() = 1000000 v2.capacity() = 1000 [Copy-op= and move-op=] x1.GetV().capacity() = 1000000 x2.GetV().capacity() = 1000 After x1 = x2: x1.GetV().capacity() = 1000000 x2.GetV().capacity() = 1000
compilable mẫu mã sau (thử nghiệm với VS2010 SP1/VC10):
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
class X
{
public:
X()
{
}
explicit X(const size_t initialCapacity)
{
m_v.reserve(initialCapacity);
}
X(const X& other)
: m_v(other.m_v)
{
}
X(X&& other)
: m_v(move(other.m_v))
{
}
void SetV(const vector<double>& v)
{
m_v = v;
}
const vector<double>& GetV() const
{
return m_v;
}
#ifdef TEST_COPY_AND_SWAP
//
// Implement a unified op= with copy-and-swap idiom.
//
X& operator=(X other)
{
swap(*this, other);
return *this;
}
friend void swap(X& lhs, X& rhs)
{
using std::swap;
swap(lhs.m_v, rhs.m_v);
}
#else
//
// Implement copy op= and move op= separately.
//
X& operator=(const X& other)
{
if (this != &other)
{
m_v = other.m_v;
}
return *this;
}
X& operator=(X&& other)
{
if (this != &other)
{
m_v = move(other.m_v);
}
return *this;
}
#endif
private:
vector<double> m_v;
};
// Test vector assignment from a small vector to a vector with big capacity.
void Test1()
{
vector<double> v1;
v1.reserve(1000*1000);
vector<double> v2(1000);
cout << "v1.capacity() = " << v1.capacity() << '\n';
cout << "v2.capacity() = " << v2.capacity() << '\n';
v1 = v2;
cout << "\nAfter copy v1 = v2:\n";
cout << "v1.capacity() = " << v1.capacity() << '\n';
cout << "v2.capacity() = " << v2.capacity() << '\n';
}
// Similar to Test1, but now vector is a data member inside a class.
void Test2()
{
#ifdef TEST_COPY_AND_SWAP
cout << "[Copy-and-swap]\n\n";
#else
cout << "[Copy-op= and move-op=]\n\n";
#endif
X x1(1000*1000);
vector<double> v2(1000);
X x2;
x2.SetV(v2);
cout << "x1.GetV().capacity() = " << x1.GetV().capacity() << '\n';
cout << "x2.GetV().capacity() = " << x2.GetV().capacity() << '\n';
x1 = x2;
cout << "\nAfter x1 = x2:\n";
cout << "x1.GetV().capacity() = " << x1.GetV().capacity() << '\n';
cout << "x2.GetV().capacity() = " << x2.GetV().capacity() << '\n';
}
int main()
{
Test1();
cout << '\n';
Test2();
}
@juanchopanza: Giống hệt nhau. – Puppy
@DeadMG Yep, tệ của tôi. – juanchopanza
@juanchopanza: Nó không tạo ra bất kỳ sự khác biệt nào. Cũng lưu ý rằng [mô hình điển hình của thành ngữ sao chép và hoán đổi] (http://stackoverflow.com/a/3279550/1629821) là "miễn phí" 'hoán đổi()'. –