本 Wiki 开启了 HTTPS。但由于同 IP 的 Blog 也开启了 HTTPS,因此本站必须要支持 SNI 的浏览器才能浏览。为了兼容一部分浏览器,本站保留了 HTTP 作为兼容。如果您的浏览器支持 SNI,请尽量通过 HTTPS 访问本站,谢谢!
这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录前一修订版 | |||
cs:programming:cpp:cpp_primer:answers:chpt_13 [2024/01/14 13:46] – 移除 - 外部编辑 (未知日期) 127.0.0.1 | cs:programming:cpp:cpp_primer:answers:chpt_13 [2024/01/14 13:46] (当前版本) – ↷ 页面programming:cpp:cpp_primer:answers:chpt_13被移动至cs:programming:cpp:cpp_primer:answers:chpt_13 codinghare | ||
---|---|---|---|
行 1: | 行 1: | ||
+ | ======Chapter.13====== | ||
+ | Answers for chapter 13 | ||
+ | ---- | ||
+ | ====Ex.13.1-13.10==== | ||
+ | ==ex.13.1== | ||
+ | > | ||
+ | A constructor is the copy constructor if its first parameter is a reference to the class type and any additional parameters have default values. A copy constructor will be used in following situations: | ||
+ | * When using an '' | ||
+ | * When passing an object as an argument to a parameter of non-reference type | ||
+ | * When returning an object from a function that has a non-reference return type | ||
+ | * When using brace initialization in an array, or the members of an aggregate class. | ||
+ | * Some allocating operation in a container, such as '' | ||
+ | ==ex.13.2== | ||
+ | > | ||
+ | <code cpp> | ||
+ | Sales_data:: | ||
+ | </ | ||
+ | illegal. | ||
+ | As a result of providing a copy of // | ||
+ | ==ex.13.3== | ||
+ | > | ||
+ | The copying of those classes will be done using memberwise-coping, | ||
+ | Additionally, | ||
+ | ==ex.13.4== | ||
+ | > | ||
+ | <code cpp> | ||
+ | Point global; | ||
+ | Point foo_bar(Point arg) | ||
+ | { | ||
+ | Point local = arg, *heap = new Point(global); | ||
+ | *heap = local; | ||
+ | Point pa[ 4 ] = { local, *heap }; | ||
+ | return *heap; | ||
+ | } | ||
+ | </ | ||
+ | Fragments use the copy constructor as shown below: | ||
+ | <code cpp> | ||
+ | Point foo_bar(Point arg) //passing an object as an argument | ||
+ | Point local = arg; // using an = to initialize a object | ||
+ | *heap = new Point(global); | ||
+ | *heap = local; // using an = to initialize a object | ||
+ | Point pa[ 4 ] = { local, *heap }; //using brace initialization in an array | ||
+ | return *heap; //returning an object from a function | ||
+ | </ | ||
+ | ==ex.13.5== | ||
+ | > | ||
+ | <code cpp> | ||
+ | class HasPtr { | ||
+ | public: | ||
+ | HasPtr(const std::string &s = std:: | ||
+ | ps(new std:: | ||
+ | private: | ||
+ | std::string *ps; | ||
+ | int i; | ||
+ | }; | ||
+ | </ | ||
+ | CODE: [[https:// | ||
+ | ==ex.13.6== | ||
+ | > | ||
+ | <color # | ||
+ | The copy-assignment operator is a function called '' | ||
+ | \\ \\ <color # | ||
+ | when assignment is needed. | ||
+ | \\ \\ <color # | ||
+ | It assigns each non-static member of the right-hand object to the corresponding member of the left-hand object using the copy-assignment operator for the type of that member. | ||
+ | \\ \\ <color # | ||
+ | The compiler generates a synthesized copy-assignment operator for a class if the class does not define its own. | ||
+ | ==ex.13.7== | ||
+ | > | ||
+ | It behaves as if it were a copy constructor, | ||
+ | ==ex.13.8== | ||
+ | > | ||
+ | CODE: [[https:// | ||
+ | ==ex.13.9== | ||
+ | > | ||
+ | <color # | ||
+ | A destructor is a special member function that is called when the lifetime of an object ends. The purpose of the destructor is to free the resources that the object may have acquired during its lifetime. | ||
+ | \\ \\ <color # | ||
+ | The members are automatically destroyed after the synthesized destructor body is run. | ||
+ | \\ \\ <color # | ||
+ | The compiler defines a synthesized destructor for any class that does not define its own destructor. | ||
+ | ==ex.13.10== | ||
+ | > | ||
+ | All members will be destroyed except for the vector shared by //StrBlob// and // | ||
+ | |||
+ | ====Ex.13.11-13.20==== | ||
+ | ==ex.13.11== | ||
+ | > | ||
+ | CODE: [[https:// | ||
+ | ==ex.13.12== | ||
+ | > | ||
+ | <code cpp> | ||
+ | bool fcn(const Sales_data *trans, Sales_data accum) | ||
+ | { | ||
+ | Sales_data item1(*trans), | ||
+ | return item1.isbn() != item2.isbn(); | ||
+ | } | ||
+ | </ | ||
+ | 3 times. '' | ||
+ | ==ex.13.13== | ||
+ | > | ||
+ | <code cpp> | ||
+ | struct X { | ||
+ | X() {std::cout << " | ||
+ | X(const X&) {std::cout << " | ||
+ | }; | ||
+ | </ | ||
+ | CODE: [[https:// | ||
+ | ==ex.13.14== | ||
+ | > | ||
+ | <code cpp> | ||
+ | void f (numbered s) { cout << s.mysn << endl; } | ||
+ | numbered a, b = a, c = b; | ||
+ | f(a); f(b); f(c); | ||
+ | </ | ||
+ | All of them will print out the number in '' | ||
+ | <code cpp> | ||
+ | b = a; // b.mysn = a.mysn | ||
+ | c = b; // c.mysn = b.mysn | ||
+ | </ | ||
+ | ==ex.13.15== | ||
+ | > | ||
+ | Yes, it changes the output. Because we use a custom copy constructor instead of a synthesized one, as a result, whenever we copy an object, the custom copy constructor will generate a new S/N number and copy it to the newly-created object, such as: | ||
+ | <code cpp> | ||
+ | numbered a; // a.mysn = SN1; | ||
+ | b = a; // b.mysn = SN2, SN2 is generated by the copy constructor | ||
+ | c = b; // c.mysn = SN3, SN3 is generated by the copy constructor | ||
+ | </ | ||
+ | ==ex.13.16== | ||
+ | > | ||
+ | If we are comparing with the result we have in the '' | ||
+ | ==ex.13.17== | ||
+ | > | ||
+ | CODE: [[https:// | ||
+ | [[https:// | ||
+ | [[https:// | ||
+ | ==ex.13.18== | ||
+ | > | ||
+ | CODE: [[https:// | ||
+ | | ||
+ | ==ex.13.19== | ||
+ | > | ||
+ | Yes. Sometimes we might need to create a document for an employee based on another employee' | ||
+ | CODE: [[https:// | ||
+ | | ||
+ | |||
+ | ==ex.13.20== | ||
+ | > | ||
+ | There are two private members in the '' | ||
+ | There are three private members in the '' | ||
+ | ====Ex.13.21-13.30==== | ||
+ | ==ex.13.21== | ||
+ | > | ||
+ | No. These classes work well with the synthesized constructor. Furthermore, | ||
+ | ==ex.13.22== | ||
+ | > | ||
+ | CODE: [[https:// | ||
+ | ==ex.13.23== | ||
+ | > | ||
+ | my strategy to solve the self-assignment: | ||
+ | <code cpp> | ||
+ | if (this == &rhs) | ||
+ | return *this; | ||
+ | </ | ||
+ | By doing this, we won't need to worry about deleting '' | ||
+ | ==ex.13.24== | ||
+ | > | ||
+ | \\ \\ <color # | ||
+ | The memory allocated by the '' | ||
+ | \\ \\ <color # | ||
+ | In this case, the synthesized copy constructor will simply copy the string pointer to the newly created object, making their pointer members point to the same string. When both the new object and the original object are destroyed, an undefined result will be presented since the destructor has freed the same string twice. | ||
+ | ==ex.13.25== | ||
+ | > | ||
+ | In order to copy the entire class content from one '' | ||
+ | Therefore, we only need to allocate a new space for the copy of the vector, and then apply a new shared_ptr to manage it. | ||
+ | \\ \\ | ||
+ | Due to copy & assignment placing the original '' | ||
+ | ==ex.13.26== | ||
+ | > | ||
+ | CODE: [[https:// | ||
+ | | ||
+ | ==ex.13.27== | ||
+ | > | ||
+ | CODE: [[https:// | ||
+ | | ||
+ | ==ex.13.28== | ||
+ | > | ||
+ | <code cpp> | ||
+ | //a) | ||
+ | class TreeNode { | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | }; | ||
+ | //b) | ||
+ | class BinStrTree { | ||
+ | private: | ||
+ | | ||
+ | }; | ||
+ | </ | ||
+ | CODE: [[https:// | ||
+ | | ||
+ | ==ex.13.29== | ||
+ | > | ||
+ | In this example, we are exchanging built-in types (pointer and int). As we don't define //swap()// for these types, we call the '' | ||
+ | ==ex.13.30== | ||
+ | > | ||
+ | CODE: [[https:// | ||
+ | | ||
+ | |||
+ | ====Ex.13.31-13.40==== | ||
+ | ==ex.13.31== | ||
+ | > | ||
+ | CODE: [[https:// | ||
+ | | ||
+ | |||
+ | ==ex.13.32== | ||
+ | > | ||
+ | No. The swap() function could improve performance because it avoids underlying copying by swapping a pointer rather than data. Since our pointlike class is all about exchanging pointers and built-in types, I can't see how it would benefit from '' | ||
+ | ==ex.13.33== | ||
+ | > | ||
+ | //save()// and // | ||
+ | ==ex.13.34== | ||
+ | > | ||
+ | CODE: [[https:// | ||
+ | | ||
+ | Nots: If the destructor wasn't setup properly for both classes, do not test the function that contains pointer register on both sides (e.g., the //save()// function). You might get a double free() error if only a set of pointers was unregistered from another. I guess the // | ||
+ | ==ex.13.35== | ||
+ | > | ||
+ | The relationship between a message and a folder is maintained by two sets of pointers. Synthesized copy members cannot keep track of these ties due to the fact that they cannot update the pointers. The accessing operation will run into serious problems if the relationship is not updated properly. \\ \\ | ||
+ | Suppose we deleted a message and didn't clean the pointer of the message in every folder, we could still see the message existed in the folder after deleting, but accessing it by the pointer would be an undefined behavior. | ||
+ | ==ex.13.36== | ||
+ | > | ||
+ | CODE: [[https:// | ||
+ | | ||
+ | ==ex.13.37== | ||
+ | > | ||
+ | Please check the files in 13.36. | ||
+ | ==ex.13.38== | ||
+ | > | ||
+ | In comparison to the copy-assignment operator, the copy-swap operator is more expensive. By far, the biggest additional cost comes from the pointer register: | ||
+ | * The first part of the additional cost comes from the parameter. As the copy-swap takes the copy of the object as an argument, it invokes the copy constructor, | ||
+ | * The second part of the additional cost comes from the swap itself. In our implementation, | ||
+ | Ultimately, the copy-swap operator takes five loops to take care of the pointer register; in contrast, the copy-assignment operation takes only two loops, not to mention the possibly expensive in using '' | ||
+ | ==ex.13.39== | ||
+ | > | ||
+ | CODE: [[https:// | ||
+ | | ||
+ | ==ex.13.40== | ||
+ | > | ||
+ | CODE: [[https:// | ||
+ | | ||
+ | ====Ex.13.41-13.50==== | ||
+ | ==ex.13.41== | ||
+ | > | ||
+ | As we use the prefix version, the element pointed by the off-the-end pointer won't be constructed, | ||
+ | < | ||
+ | ==ex.13.42== | ||
+ | > | ||
+ | CODE: [[https:// | ||
+ | | ||
+ | | ||
+ | ==ex.13.43== | ||
+ | > | ||
+ | It is my preferred version, since the loop order doesn' | ||
+ | CODE: [[https:// | ||
+ | | ||
+ | ==ex.13.44== | ||
+ | > | ||
+ | CODE: [[https:// | ||
+ | | ||
+ | ==ex.13.45== | ||
+ | > | ||
+ | * //lvalue//, emphasizing the identity, is usually an object that can be addressed. | ||
+ | * //rvalue//, emphasis the value, is usually is a temporary that can't be addressed. | ||
+ | Therefore, the difference between an //rvalue// reference and an //lvalue// reference is that an //rvalue// reference refers to an object that is about to expire, whereas an //lvalue// reference refers to one that will exist for its entirety. | ||
+ | ==ex.13.46== | ||
+ | > | ||
+ | < | ||
+ | int f(); | ||
+ | vector< | ||
+ | |||
+ | //&&, | ||
+ | int? r1 = f(); | ||
+ | |||
+ | //&, vi[0] subscript operation yields a lvalue | ||
+ | int? r2 = vi[0]; | ||
+ | |||
+ | //&, value expression is a lvalue | ||
+ | int? r3 = r1; | ||
+ | |||
+ | //&&, | ||
+ | int? r4 = vi[0] * f(); | ||
+ | </ | ||
+ | ==ex13.47== | ||
+ | > | ||
+ | CODE: [[https:// | ||
+ | | ||
+ | ==ex.13.48== | ||
+ | > | ||
+ | <code cpp> | ||
+ | vector< | ||
+ | myVS.push_back(" | ||
+ | myVS.push_back(" | ||
+ | myVS.push_back(" | ||
+ | myVS.push_back(" | ||
+ | myVS.push_back(" | ||
+ | </ | ||
+ | Looks like the program calls copy constructor each time we do a // | ||
+ | ==ex.13.49== | ||
+ | > | ||
+ | //Message// : [[https:// | ||
+ | | ||
+ | //String//: [[https:// | ||
+ | | ||
+ | //StrVec//: [[https:// | ||
+ | | ||
+ | ==ex.13.50== | ||
+ | > | ||
+ | <code cpp> | ||
+ | vector< | ||
+ | myVS.push_back(" | ||
+ | myVS.push_back(" | ||
+ | myVS.push_back(" | ||
+ | myVS.push_back(" | ||
+ | myVS.push_back(" | ||
+ | </ | ||
+ | Looks like // | ||
+ | ====Ex.13.51-13.58==== | ||
+ | ==ex.13.51== | ||
+ | > | ||
+ | <code cpp> | ||
+ | unique_ptr< | ||
+ | unique_ptr< | ||
+ | // . . . | ||
+ | return ret; | ||
+ | } | ||
+ | </ | ||
+ | The entire process calls only the move constructor. The //clone()// takes an rvalue argument, so the move constructor moves the content of '' | ||
+ | ==ex.13.52== | ||
+ | > | ||
+ | <code cpp> | ||
+ | class HasPtr { | ||
+ | |||
+ | public: | ||
+ | HasPtr(HasPtr && | ||
+ | HasPtr& operator=(HasPtr rhs) | ||
+ | { swap(*this, rhs); return *this; } | ||
+ | }; | ||
+ | </ | ||
+ | <code cpp> | ||
+ | //1. a temporary HasPtr is allocated, as the copy of hp2 | ||
+ | //2. hp is freed | ||
+ | //3. hp and hp2(copy) are swapped, now the hp own the copy | ||
+ | hp = hp2; | ||
+ | |||
+ | //1.hp is freed, since move constructor doesn' | ||
+ | //2.hp and the copy are swapped. After swapping, hp now points to the memory that used to owned by hp2 | ||
+ | //3.The pointer in hp2(copy) is nulled for the destructor. | ||
+ | hp = std:: | ||
+ | </ | ||
+ | ==ex.13.53== | ||
+ | > | ||
+ | suppose we have the following test operations: | ||
+ | <code cpp> | ||
+ | HasPtr hp(" | ||
+ | HasPtr hp2, hp3; | ||
+ | hp2 = hp; | ||
+ | hp3 = std:: | ||
+ | </ | ||
+ | using copy-swap assignment operator will: | ||
+ | * call copy-consturctor and copy-swap assignment operator on '' | ||
+ | * call move-consturctor and copy-swap assignment operator on '' | ||
+ | using copy-assignment operator plus move-assignment operator will: | ||
+ | * call copy-assignment operator on '' | ||
+ | * call move-assignment operator on ''' | ||
+ | We can see that every time we do a copy-swap assignment, it invokes the copy / move constructor. This is because the copy-swap operator takes an argument, passes it to the '' | ||
+ | CODE : [[https:// | ||
+ | | ||
+ | Test output: | ||
+ | <code cpp> | ||
+ | /* copy-swap */ | ||
+ | Copy-CSTR called. | ||
+ | Copy-swap OP called. | ||
+ | Move-CSTR called. | ||
+ | Copy-swap OP called. | ||
+ | |||
+ | /* copy assign plus move assign */ | ||
+ | Copy-assignment OP called. | ||
+ | Move-assignment OP called. | ||
+ | </ | ||
+ | ==ex.13.54== | ||
+ | > | ||
+ | It will result in an ambiguous error. It is likely that there will be two best match candidates of '' | ||
+ | ==ex.13.55== | ||
+ | > | ||
+ | <code cpp> | ||
+ | inline void | ||
+ | strBlob:: | ||
+ | { | ||
+ | data-> | ||
+ | } | ||
+ | </ | ||
+ | ==ex.13.56== | ||
+ | > | ||
+ | <code cpp> | ||
+ | Foo Foo:: | ||
+ | { | ||
+ | Foo ret(*this); | ||
+ | return ret.sorted(); | ||
+ | } | ||
+ | </ | ||
+ | The function is expecting to return an rvalue, thus it needs to call the ravlue version of '' | ||
+ | ==ex.13.57== | ||
+ | > | ||
+ | <code cpp> | ||
+ | Foo Foo:: | ||
+ | </ | ||
+ | This version works well. In this case, '' | ||
+ | ==ex.13.58== | ||
+ | > | ||
+ | CODE : [[https:// | ||
+ | |