c++ 副本构造函数 函数实参 形参 c++入门经典 11 17 章
11.4.9
c++默认生成副本构造函数,但是有指针时会造成一个内存被两个指针同时指向的问题。
Box::Box(Box box){}
- 这个实参时按值传递。它是个Box对象,所以编译器需要调用 Box的副本构造函数,制作这个副本。而这个副本构造函数又是按值传递的,又需要再次调用副本构造函数,造成递归。为了避免,需要用引用。避免递归。
- 如果不用const,这个参数就不能接收const的对象实参,比如 const Box b; Box c{b}. 编译时会出错。
所以,所以构造类型都一样:
TYPE::TYPE(const TYPE& object){ ... }
注意,所以实参的传递是用 copy constructor,而不是 assignment operator。
- 申明时的赋值其实是拷贝构造:比如: Box a; Box b=a;等价于 Box b{a} 或者 Box b(a);
- 在函数的参数传递时:
void f(Box p){};
Box a;
f(a);
上面语句相当于:Box p=a; 等价于 Box p{a}。这里不是调用的赋值构造,而是调用拷贝构造函数。
f(Box()) 由于传递进去个临时变量,会调用移动构造函数。
参考示例,好好体会:
示例1:push_back的左值引用和右值引用分开版:
把下面的代码中: #if 1全变成 #if 0.
示例2:push_back用一个 传值 的版本
// Exercising two overloads of push_back(): one for lvalue arguments, and one for rvalue arguments #include <stdexcept> // For standard exception types #include <string> // For std::to_string() #include <utility> // For std::as_const() #include <iostream> // For std::cout (used for debugging output) template <typename T> class Array { private: T* elements; // Array of type T size_t size; // Number of array elements public: explicit Array(size_t arraySize = 0); // Constructor Array(const Array& array); // Copy constructor Array(Array&& array); // Move constructor ~Array(); // Destructor Array& operator=(const Array& rhs); // Copy assignment operator Array& operator=(Array&& rhs); // Move assignment operator #if 1 void push_back(T value); // Add a new element (copied) #else void push_back(const T& value); // Add a new element (copied) void push_back(T&& value); // Add a new element (moved) #endif T& operator[](size_t index); // Subscript operator const T& operator[](size_t index) const; // Subscript operator-const arrays size_t getSize() const { return size; } // Accessor for size void swap(Array& other) noexcept; // noexcept swap member function }; // Conventional noexcept swap non-member function template <typename T> void swap(Array<T>& one, Array<T>& other) noexcept { one.swap(other); // Forward to public member function } // Constructor template <typename T> // This is a template with parameter T Array<T>::Array(size_t arraySize) : size{ arraySize }, elements{ new T[arraySize] } {} // Copy constructor template <typename T> Array<T>::Array(const Array& array) : Array{ array.size } { std::cout << "Array of " << size << " elements copied" << std::endl; for (size_t i{}; i < size; ++i) elements[i] = array.elements[i]; } // Move constructor template <typename T> Array<T>::Array(Array&& moved) : size{ moved.size }, elements{ moved.elements } { std::cout << "Array of " << size << " elements moved" << std::endl; moved.elements = nullptr; // Prevent moved from calling delete[] on elements } // Destructor template <typename T> Array<T>::~Array() { delete[] elements; } // const subscript operator template <typename T> const T& Array<T>::operator[](size_t index) const { if (index >= size) throw std::out_of_range{ "Index too large: " + std::to_string(index) }; return elements[index]; } // Non-const subscript operator in terms of const one // Uses the 'const-and-back-again' idiom template <typename T> T& Array<T>::operator[](size_t index) { return const_cast<T&>(std::as_const(*this)[index]); } #if 1 template <typename T> void Array<T>::push_back(T element) { Array<T> newArray(size + 1); // Allocate a larger Array<> for (size_t i = 0; i < size; ++i) // Move all existing elements... newArray[i] = std::move(elements[i]); newArray[size] = std::move(element); // Move the new one as well... swap(newArray); // ... and swap! (noexcept) } #else // Add a new element by copying it. // This overload is selected whenever an lvalue expression is passed to push_back(). template <typename T> void Array<T>::push_back(const T& element) { Array<T> newArray(size + 1); // Allocate a larger Array<> for (size_t i = 0; i < size; ++i) // Move all existing elements... newArray[i] = std::move(elements[i]); newArray[size] = element; // Copy the new one... swap(newArray); // ... and swap! (noexcept) } // Add a new element by moving it. // This overload is selected whenever an rvalue expression is passed to push_back(). template <typename T> void Array<T>::push_back(T&& element) { Array<T> newArray(size + 1); // Allocate a larger Array<> for (size_t i = 0; i < size; ++i) // Move all existing elements... newArray[i] = std::move(elements[i]); newArray[size] = std::move(element); // Move the new one as well... swap(newArray); // ... and swap! (noexcept) } #endif // Copy assignment operator in terms of the copy constructor // Uses the 'copy-and-swap' idiom. template <typename T> Array<T>& Array<T>::operator=(const Array& rhs) { Array<T> copy{ rhs }; // Copy ... (could go wrong and throw an exception) swap(copy); // ... and swap! (noexcept) return *this; // Return lhs } // Move assignment operator template <typename T> Array<T>& Array<T>::operator=(Array&& rhs) { std::cout << "Array of " << rhs.size << " elements moved (assignment)" << std::endl; if (this != &rhs) // prevent trouble with self-assignments { delete[] elements; // delete[] all existing elements elements = rhs.elements; // copy the elements pointer and the size size = rhs.size; rhs.elements = nullptr; // make sure rhs does not delete[] elements } return *this; // return lhs } // noexcept swap member function template <typename T> void Array<T>::swap(Array& other) noexcept { std::swap(elements, other.elements); std::swap(size, other.size); } // Construct an Array<> of a given size, filled with some arbitrary string data Array<std::string> buildStringArray(const size_t size) { Array<std::string> result{ size }; for (size_t i = 0; i < size; ++i) result[i] = "You should learn from your competitor, but never copy. Copy and you die."; return result; } int main() { Array<Array<std::string>> array_of_arrays; Array<std::string> array{ buildStringArray(1'000) }; array_of_arrays.push_back(array); // Push an lvalue array.push_back("One more for good measure"); std::cout << std::endl; array_of_arrays.push_back(std::move(array)); // Push an rvalue }