365体育网页版在线登录官网

21天学通C++学习笔记

第一章1.6 作业1.6.1 测试 解释器和编译器有什么不同? 答:解释器是一种对代码(或字节码)进行解释并执行相应的工具;编译器将代码作为输

21天学通C++学习笔记

第一章1.6 作业1.6.1 测试

解释器和编译器有什么不同?

答:解释器是一种对代码(或字节码)进行解释并执行相应的工具;编译器将代码作为输入,并生成目标文件。就C++而言,编译和链接后,将得到一个可执行文件,处理器可直接执行它,而无需做进一步解释。

链接器的作用是什么?

答:链接器的作用是将多个目标文件(.o 或 .obj)和库文件组合在一起,生成一个可执行文件或库。

正常的开发周期包括哪些步骤?

答:

需求分析:明确项目目标、用户需求和功能要求。​系统设计:设计架构、模块和数据库结构。开发实现:编写代码,完成各功能模块。测试:进行单元测试、集成测试、功能测试等,确保质量。部署上线:将产品部署到目标环境并上线运行。维护与迭代:修复问题、优化功能、发布新版本。

1.6.2 练习

阅读下面的程序,在不运行它的情况下猜测其功能

12345678910#includeint main(){ int x = 8; int y = 6; std::cout << std::endl; std::cout << x - y << " " << x * y << " " << x + y << std::endl; std::cout << std::endl; return 0;}

​ **答:**输出x与y加、乘和减三个计算结果

输入练习1中的程序,然后编译并链接它。它做什么?与您的猜测相符吗?

下面的程序存在什么样的错误?

123456includeint main(){ std::cout << "Hello Buggy World \n"; return 0;}

​ 答:没有在include前加上#

4.修复练习3中的程序的错误,重新编译、链接并运行它。它做什么?

123456#includeint main(){ std::cout << "Hello Buggy World \n"; return 0;}

​ 答:输出Hello Buggy World\n

第二章2.4 C++函数123456789101112131415161718192021222324//程序清单2.4 声明、定义和调用函数,该函数演示了std::cout的功能#includeusing namespace std;int DemoConsoleOutput();int main(){ DemoConsoleOutput(); return 0;}int DemoConsoleOutput(){ cout << "This is a simple string literal" << endl; cout << "Writing number five:" << 5 << endl; cout << "Performing division 10 / 5= " << 10/5 << endl; cout << "Pi when approximated is 22 / 7 = " << 22 / 7 << endl; cout << "Pi is 22 / 7 = " << 22.0 / 7 << endl; return 0;}

123456789101112131415161718192021//程序清单2.5 使用函数的返回值#includeusing namespace std;int DemoConsoleOutput();int main(){ return DemoConsoleOutput();}int DemoConsoleOutput(){ cout << "This is a simple string literal" << endl; cout << "Writing number five:" << 5 << endl; cout << "Performing division 10 / 5= " << 10/5 << endl; cout << "Pi when approximated is 22 / 7 = " << 22 / 7 << endl; cout << "Pi is 22 / 7 = " << 22.0 / 7 << endl; return 0;}

2.5 使用std::cin和std::cout执行基本输入输出操作123456789101112131415161718192021//程序清单2.6 使用cin和cout显示用户的数字输入和文本输出#include#includeusing namespace std;int main(){ int inputNumber; cout << "Enter an integer:"; cin >> inputNumber; cout << "Enter your name:"; string inputName; cin >> inputName; cout << inputName << " entered " << inputNumber << endl; return 0;}

2.8作业2.8.1 测验

声明Int main()有何问题?

​ 答:1.Int不是数据类型关键字不能用作返回值

2.main函数只能有一个,不能重载

注释可以超过一行吗?

答:可以。使用/**/多行注释或使用预编译指令

2.8.2 练习

查错:输入下面的程序并编译它。为什么不能通过编译?如何修复?

12345#includevoid main(){ std::Cout << Is there a bug here";}

答:1.输出对象书写错误,应改为cout

​ 2.字符串字面值常量的双引号不全,应改为“Is there a bug here”

修复练习1中的错误,然后重新编译、链接并运行它。

12345#includevoid main(){ std::cout << "Is there a bug here";}

修改程序清单2.4,以演示减法和乘法

1234567891011121314151617181920212223#includeusing namespace std;int DemoConsoleOutput();int main(){ DemoConsoleOutput(); return 0;}int DemoConsoleOutput(){ cout << "This is a simple string literal" << endl; cout << "Writing number five:" << 5 << endl; cout << "Performing subtraction 10 - 5= " << 10 - 5 << endl; cout << "Pi when approximated is 22 * 7 = " << 22 * 7 << endl; cout << "Pi is 22 * 7 = " << 22.0 * 7 << endl; return 0;}

第三章3.1 什么是变量3.1.2声明和访问变量

变量的定义

VariableType VariableName;

VariableType VariableName = InitialValue;

12345678910111213141516171819202122//3.1 程序清单使用变量存储数字及其相乘结果#includeusing namespace std;int main(){ cout << "This program will help you multiply tow numbers" << endl; cout << "Enter the first number:"; int firstNumber; cin >> firstNumber; cout << "Enter the second number:"; int secondNumber; cin >> secondNumber; int multiplicationResult = firstNumber * secondNumber; cout << firstNumber << " x " << secondNumber << " = " << multiplicationResult << endl; return 0;}

3.1.5 全局变量

123456789101112131415161718192021222324252627282930313233//程序清单3.3 使用全局变量#includeusing namespace std;int firstNumber = 0;int secondNumber = 0;int multiplicationResult = 0;void MultiplyNumbers(){ cout << "Enter the first number:"; cin >> firstNumber; cout << "Enter the second number:"; cin >> secondNumber; cout << firstNumber << " x " << secondNumber << " = " << multiplicationResult << endl;}int main(){ cout << "This program will help you multiply tow numbers" << endl; MultiplyNumbers(); cout << "DisplayNumbers()"; cout << firstNumber << " x " << secondNumber << " = " << multiplicationResult << endl; return 0;}

3.1.6 命名约定

Pascal拼写法:每个单词首字母大写

驼峰拼写法:第一个单词的首字母小写,后面单词首字母大写

匈牙利表示法:在变量名开头包含指出变量类型的字符

下划线拼写法:每一个单词用下划线隔开

3.2 编译器支持的常见C++变量类型3.2.6 选择正确的数据类型以避免发生溢出错误

12345678910111213141516//程序清单3.4 演示有符号整型变量和无符号中心变量溢出的负面影响#includeusing namespace std;int main(){ unsigned short uShortValue = 65535; cout << "Incrememting unsigned short " << uShortValue << "gives: "; cout << ++uShortValue << endl; short signedShort = 32767; cout << "Incrememting signed short " << signedShort << "gives:"; cout << ++signedShort << endl; return 0;}

3.3 使用sizeof确定变量长度3.3.1 使用列表初始化避免缩窄转换错误

12345int largeNum = 5000000;short anotherNum{ largeNum }; //error! Amend typesint anoterNUm{ largeNum }; //OKfloat someFloat{ largeNum }; //error! An int may be narrowedfloat someFloat{ 5000000 }; //OK! 5000000 can be accomodated

3.4使用auto自动推导类型1234567891011121314151617//程序清单3.6 使用关键字auto依靠编译器的类型推导功能#includeusing namespace std;int main(){ auto coinFlippedHeads = true; auto largeNumber = 2500000000000; cout << "coinFlippedHeads = " << coinFlippedHeads; cout << " , sizeof(coinFlippedHeads) = " << sizeof(coinFlippedHeads) << " " << typeid(coinFlippedHeads).name() << endl; cout << "largeNumber = " << largeNumber; cout << " , sizeof(largeNumber) = " << sizeof(largeNumber) << " " << typeid(largeNumber).name() << endl; return 0;}

3.6 什么是常量c++中的常量

字面常量;

使用关键字const声明的常量;

使用关键字constexpr声明的常量表达式(C++11 新增的);

使用关键字enum (class)声明的枚举常量;

使用#define定义的常量(不推荐);

3.6.2 使用const将变量声明为常量

​ 通用的声明方法:const typeName constantName = value;

1234567891011//程序清单3.7 声明一个名为pi的变量#includeusing namespace std;int main(){ const double pi = 22.0 / 7; cout << "The value of constant pi is: " << pi << endl; return 0;}

3.6.3 使用constexpr 定义常量表达式

1234567891011121314151617//程序清单3.8 使用常量表达式来计算pi的值#includeusing namespace std;constexpr double GetPi() { return 22.0 / 7; }constexpr double TwicePi() { return 2 * GetPi(); }int main(){ const double pi = 22.0 / 7; cout << "constant pi contains value " << pi << endl; cout << "constexpr GetPi() returns value " << GetPi() << endl; cout << "constexpr TwicePi() return value " << TwicePi() << endl; return 0;}

3.6.4 枚举

123456789101112131415161718192021222324//程序清单3.9 使用枚举量指示基本方位#includeusing namespace std;enum CardinalDirections{ North = 25, South, West, East,};int main(){ cout << " Displaying directions and their symbolic values" << endl; cout << "North: " << North << endl; cout << "South: " << South << endl; cout << "West: " << West << endl; cout << "East: " << East << endl; CardinalDirections windDirection = South; cout << "Variable windDirection = " << windDirection << endl; return 0;}

作业3.10.1 测验

有符号整型和无符号整型有何不同?

答:它们在内存中的最高位表示不同,无符号的最高位表示数值,有符号的最高位表示符号位

为何不应使用#define来声明常量?

答:#define定义的常量只是进行文本替换,不进行安全检查,这是不安全的。

为何要对变量进行初始化?

答:初始化变量是一个良好的编程习惯,可以增强代码的可读性、可靠性和安全性,避免意外行为的发生。

给定如下枚举类型,Quee的值是多少?

1enum YourCards {Ace, Jack, Queen, King};

答:2

下述变量名有何问题?

1int Interger = 0;

答:编译没有问题,但是变量名不能够正确表达意思。

3.10.2 练习

修改测试题4中的枚举类型,让Queen的值为45

12enum YourCards {Ace, Jack, Queen = 45, King}; //方法一 0,1,45,46enum YourCards {Ace = 43, Jack, Queen = 45, King}; //方法二 43,44,45,36

编写一个程序,证明unsigned int 和 int 变量的长度,且它们对比long变量短。

1234567891011#includeusing namespace std;int main(){ cout << "unsigned int " << sizeof(unsigned int) << endl; cout << "int " << sizeof(int) << endl; cout << "long " << sizeof(long) << endl; return 0;}

编写一个程序,让用户输入圆的半径,并计算其面积和周长?

12345678910111213141516171819#includeusing namespace std;const double pi = 3.14;int main(){ cout << "Enter your radius:"; double radius = 0.0; cin >> radius; double circumference = 2 * radius * pi; double area = radius * radius * pi; cout << "circumference = " << circumference << endl; cout << "area = " << area << endl; return 0;}

在练习3中,如果将面积和周长存储在int中,输出将有何不同?

答:会损失小数的精度,输出一个整型

查错:下面的语句有何错误?

auto Integer

​ 答:Integer没有初值,auto无法进行推导

第四章4.1 什么是数组数组具有一下特点

数组是一系列元素

数组中所有元素都是相同类型

这组元素形成一个完整的集合

4.1.2 声明和初始化静态数组

​ 数组声明如下

​ ElementType ArrayName [constant_number of elements] = {optional initial values};

4.1.4 访问存储在数组中的数据

12345678910111213141516//程序清单 4.1 声明一个int数组并访问其元素#includeusing namespace std;int main(){ int myNumbers[5] = { 34,56,-21,5002,365 }; cout << "First element at index 0:" << myNumbers[0] << endl; cout << "Second element at index 1:" << myNumbers[1] << endl; cout << "Third element at index 2:" << myNumbers[2] << endl; cout << "Fourth element at index 3:" << myNumbers[3] << endl; cout << "Fifth element at index 4:" << myNumbers[4] << endl; return 0;}

4.1.5 修改存储在数组中的数据

12345678910111213141516171819202122232425262728//程序清单 4.2 给元素赋值#includeusing namespace std;constexpr int Square(int number) { return number * number; }int main(){ const int ARRAY_LENGTH = 5; int myNumbers[ARRAY_LENGTH] = { 5,10,0,-101,20}; int moreNumbers[Square(ARRAY_LENGTH)]; cout << "Enter index of the element to be change:"; int elementIndex = 0; cin >> elementIndex; cout << "Enter new value"; int newValue; cin >> newValue; myNumbers[elementIndex] = newValue; moreNumbers[elementIndex] = newValue; cout << "Element " << elementIndex << "in array myNumbers is: " << myNumbers[elementIndex] << endl; cout << "Element " << elementIndex << "in array moreNumbers is: " << moreNumbers[elementIndex] << endl; return 0;}

4.2 多维数组4.2.1 声明和初始化多维数组

​ 在C++中,要声明多维数组,可指定每维包含的元素数

1int solarPanels[2][3] = {{0,1,2},{3,4,5}};

4.2.2 访问多维数组中的元素

12345678910111213141516171819202122//程序清单 4.3 访问多维数组中的元素#includeusing namespace std;int main(){ int threeRowsThreeColumns[3][3] = \ {{-501, 205, 2016}, { 989,101,206 }, {303,456,596}}; cout << "Row 0:" << threeRowsThreeColumns[0][0] << " "\ << threeRowsThreeColumns[0][1] << " "\ << threeRowsThreeColumns[0][2] << endl; cout << "Row 1:" << threeRowsThreeColumns[1][0] << " "\ << threeRowsThreeColumns[1][1] << " "\ << threeRowsThreeColumns[1][2] << endl; cout << "Row 2:" << threeRowsThreeColumns[2][0] << " "\ << threeRowsThreeColumns[2][1] << " "\ << threeRowsThreeColumns[2][2] << endl; return 0;}

4.3 动态数组1234567891011121314151617181920212223242526//程序清单 4.4 创建int动态数组并动态插入#include#includeusing namespace std;int main(){ vector dynArray(3); dynArray[0] = 365; dynArray[1] = -421; dynArray[2] = 789; cout << "Number of integers in array: " << dynArray.size() << endl; cout << "Enter another element to insert" << endl; int newValue = 0; cin >> newValue; dynArray.push_back(newValue); cout << "Number of integers in arry: " << dynArray.size() << endl; cout << "Last element in array: " << dynArray[dynArray.size() - 1] << endl; return 0;}

4.4 C风格字符串1234567891011121314151617//程序清单 4.5 分析C风格字符串中的终止空字符#includeusing namespace std;int main(){ char sayHello[] = {'H','e','l','l','o',' ','w','o','r','l','d','\0'}; cout << sayHello << endl; cout << "Size of array: " << sizeof(sayHello) << endl; cout << "Replaceing space with null" << endl; sayHello[5] = '\0'; cout << "Size of array: " << sizeof(sayHello) << endl; return 0;}

4.5 C++字符串:使用std::string1234567891011121314151617181920212223242526272829303132//程序清单 4.7 使用std::string初始化字符串、存储用户输入、复制和拼接字符串以及字符串的长度#include#includeusing namespace std;int main(){ string greetString("Hello std::string!"); cout << greetString << endl; cout << "Enter a line of text: " << endl; string firstLine; getline(cin, firstLine); cout << " Enter another: " << endl; string secondLine; getline(cin, secondLine); cout << "Result of concatenation: " << endl; string concatString = firstLine + " " + secondLine; cout << concatString; cout << "Copy of concatentated string: " << endl; string aCopy; aCopy = concatString; cout << aCopy << endl; cout << "Length of concatString: " << concatString.size() << endl; return 0;}

4.8 作业4.8.1 测验

d对于程序清单4.1中的数组myNumbers,第一个和最后一个的索引元素分别是多少?

答:0和4

如果需要让用户输入字符串,该使用C风格的字符串吗?

答:不推荐使用,应该使用std::string,以防止出现越界的情况

在编译器看来,’\0’表示多少个字符?

答:1个

如果忘记在C风格字符串末尾添加空字符,使用它的结果将会如何?

答:会出现字符串乱码

根据程序清单4.4中的矢量的声明,尝试声明一个包含char元素的动态数组?

答:vector charArray;

4.8.2 练习

声明一个国际象棋棋盘的数组;该数组的类型为枚举,该枚举定义了可能出现在棋盘方格中的棋子。

1234567891011121314151617181920212223#include#includeusing namespace std;enum Square{ empty = 0, Pawn, Rook, Knight, Bishop, King, Queen};int main(){ Square chessBoard[8][8]; chessBoard[0][0] = chessBoard[0][7] = Rook; chessBoard[7][7] = chessBoard[7][0] = Rook; return 0;}

查错:下面的代码段有什么错误?

12int myNumbers[5] = {0};myNumbers[5] = 450;

​ 答:越界操作

查错:下面的代码段有什么错误?

12int myNumbers[5];cout << myNumbers[3];

​ 答:访问了未初始化的内存

第五章5.3 使用运算符​ 5.3.2 理解左值和右值

左值:通常是内存单元

右值:是内存单元的内容

所有左值可以用作右值,但并非所有右值都可以用作左值

​ 5.3.3 加法运算符、除法运算符、减法运算符、乘法运算符和取模运算符

12345678910111213141516171819//程序清单5.1 演示如何对用户输入的整数执行算术运算#includeusing namespace std;int main(){ cout << "Enter two integers: " << endl; int num1 = 0, num2 = 0; cin >> num1; cin >> num2; cout << num1 << " + " << num2 << " = " << num1 + num2 << endl; cout << num1 << " - " << num2 << " = " << num1 - num2 << endl; cout << num1 << " * " << num2 << " = " << num1 * num2 << endl; cout << num1 << " / " << num2 << " = " << num1 / num2 << endl; cout << num1 << " % " << num2 << " = " << num1 % num2 << endl; return 0;}

5.3.5 前缀还是后缀

前缀:先将右值递增或递减,再将结果赋给左值。

后缀:先将右值赋给左值,再将右值递减或递增

123456789101112131415161718192021222324252627282930//程序清单5.2 前缀运算符和后缀运算符之间的差别#includeusing namespace std;int main(){ int startValue = 101; cout << "Sart value of integer being operated: " << startValue << endl; int postfixIncrement = startValue++; cout << "Result of Postfix Increment = " << postfixIncrement << endl; cout << "After Postfix Increment, startValue = " << startValue << endl; startValue = 101; int prefixIncrement = ++startValue; cout << "Result of prefix Increment = " << prefixIncrement << endl; cout << "After prefix Increment, startValue = " << startValue << endl; startValue = 101; int postfixDecrement = startValue--; cout << "Result of Postfix Decrement = " << postfixDecrement << endl; cout << "After Postfix Decrement, startValue = " << startValue << endl; startValue = 101; int prefixDecrement = --startValue; cout << "Result of Postfix Decrement = " << prefixDecrement << endl; cout << "After prefix Decrement, startValue = " << startValue << endl; return 0;}

作业5.6.1 测验

编写将两个数相除的应用程序时,将变量声明为哪种数据类型更合适,int还是float?

答:float,防止精度丢失。

32/7的结果是多少?

答:4

32.0/7的结果是多少?

答:4.57143

sizeof()是函数吗?

答:是关键字

我需要将一个数翻倍,再加上5,然后在翻倍,下面的代码是否正确?

int result = number << 1 + 5 << 1

答:不正确

如果两个操作数的值都为true,对其执行XOR的结果是什么?

答:false

5.6.2 练习

使用括号改善测试题5中的代码,使其清晰

答:int result = ((number << 1) + 5) << 1;

下述代码导致result的值为多少?

答:result << 7

编写一个程序,让用户输入两个布尔值,并显示其执行各种按位运算的结果。

1234567891011121314151617#includeusing namespace std;int main(){ cout << "Enter your two bool value:" << endl; bool bit1 = false; bool bit2 = false; cin >> bit1; cin >> bit2; cout << "or: " << (bit1 | bit2) << endl; cout << "and: " << (bit1 & bit2) << endl; cout << "xor: " << (bit1 ^ bit2) << endl; return 0;}

第六章6.2 在循环中执行代码6.2.5 基于范围的for循环

​ for(VarType varName : sequence){

​ //Use varName that contains an element from sequence

​ }

1234567891011121314151617181920212223242526272829303132333435363738//程序清单6.12 使用基于范围的for循环来处理数组和std::string#include#includeusing namespace std;int main(){ int sumoNums[] = { 1,101,-1,40,2040 }; for (const int& aNum : sumoNums) { cout << aNum << ' '; } cout << endl; for (auto anElement : { 5,222,110,-45,2017 }) { cout << anElement << ' '; } cout << endl; char charArray[] = { 'h','e','l','l','o' }; for (auto anElement : charArray) { cout << anElement << ' '; } cout << endl; double moreNums[] = { 3.14,-1.3,22,10101 }; for (auto anElement : moreNums) { cout << anElement << ' '; } cout << endl; string sayHello{ "Hello world!" }; for (auto anElement : sayHello) { cout << anElement << ' '; } cout << endl; return 0;}

6.7 作业

6.7.1 测验

既然不缩进也能通过编译,为什么要缩进语句块、嵌套if语句和嵌套循环?

答:缩进让代码结构更加清晰,便于阅读和理解

使用goto可以快速解决问题,为何还要避免使用它?

答:goto会破坏程序的结构,容易引发逻辑错误

可以编写计算器递减的for循环吗?这样的for循环是什么样的?

123for(int i = value; i > 0; i--){}

下面的循环有何问题?

12for(int counter = 0; counter == 10; ++counter) cout << counter << " ";

答:判断条件存在问题,应该改为counter != 10

6.7.2 练习

编写一个for循环,以倒叙的方式访问数组

12345678910111213141516#includeusing namespace std;int main(){ const int ARRAY_LENGTH = 5; int arr[ARRAY_LENGTH] = { 0 ,1, 2, 3, 4}; for (int i = ARRAY_LENGTH - 1; i >= 0; i--) { cout << arr[i] << " "; } cout << endl; return 0;}

编写一个类似于程序清单6.14的嵌套for循环,但以倒序方式将一个数组的每个元素都与另一个数组的每个元素相加。

1234567891011121314151617181920212223#includeusing namespace std;int main(){ const int ARRAY_LENGTH = 5; int arr1[ARRAY_LENGTH] = { 0 ,1, 2, 3, 4}; int arr2[ARRAY_LENGTH] = { 10,20,30,40,50 }; for (int i = ARRAY_LENGTH - 1; i >= 0; i--) { for (int j = 0; j < ARRAY_LENGTH; j++) { arr1[i] += arr2[j]; } } for (int i = 0; i < ARRAY_LENGTH; i++) { cout << arr1[i] << " "; } cout << endl; return 0;}

编写一个程序,象清单6.16那样显示斐波那契数列,但让用户指定每次显示多少个

12345678910111213141516171819202122232425262728#includeusing namespace std;int main(){ cout << "Please enter the number of items to display per line." << endl; int num = 0; cin >> num; int n1 = 0, n2 = 1; char wantMore = '\0'; cout << n1 << " " << n2 << " " << endl; do{ for (int i = 0; i < num; i++) { cout << n1 + n2 << " "; int num2Temp = n2; n2 = n1 + n2; n1 = num2Temp; } cout << endl << "Do you want more numbers(y/n)"; cin >> wantMore; } while (wantMore == 'y'); cout << "Goodbye" << endl; return 0;}

编写一个switch-case结构,指出用户选择的颜色是否出现在彩虹中。请使用枚举常量

123456789101112131415161718192021222324252627282930313233343536#includeusing namespace std;enum Colors{ Violet,Indigo,Blue,Green,Yellow,Orange,Red,Crimson,Beige,Brown,Peach,Pink,White};int main(){ cout << "Here are the available colors: " << endl; cout << "Violet,Indigo,Blue,Green,Yellow,Orange,Red,Crimson,Beige,Brown,Peach,Pink,White" << endl; cout << "Corresponding to the numbers 0 to 12." << endl; cout << "Choose one by entering code: "; int yourChoice = Blue; cin >> yourChoice; switch (yourChoice) { case Violet: case Indigo: case Blue: case Green: case Yellow: case Orange: case Red: cout << "Bingo,your choice is a Rainbow color!" << endl; break; default: cout << "The color you chose is not in the rainbow" << endl; break; } return 0;}

查错:下面的代码有何错误?

12for(int counter = 0; counter = 10; ++counter) cout << counter << " ";

答:counter=10,这条语句可能会导致死循环

查错:下面的代码有何错误?

123456int loopCounter = 0;while(loopCounter < 5);{ cout << loopCounter << " "; loopCounter++;}

答:while(loopCounter < 5);的分号会导致死循环,不会执行后面的操作。

查错:下面的代码有何错误?

123456789101112131415cout << "Enter a number between 0 and 4" << endl;int input = 0;cin >> input;switch (input){case 0:case 1:case 2:case 3:case 4: cout << "Valid input" << endl;default: cout << "Invalid input" << endl;}

答:没有在cout << “Valid input” << endl;后添加break.

第七章7.1 为何需要函数123456789101112131415161718192021222324252627282930//程序清单7.1 两个根据半径分别计算圆的面积和周长的函数#includeusing namespace std;const double Pi = 3.14159265;double Area(double radius);double Circumference(double radius);int main(){ cout << "Enter radius: "; double radius = 0; cin >> radius; cout << "Area is: " << Area(radius) << endl; cout << "Circumference is: " << Circumference(radius) << endl; return 0;}double Area(double radius){ return Pi * radius * radius;}double Circumference(double radius){ return Pi * 2 * radius;}

7.1.1 函数原型是什么

double Area(double radius);

double: 返回值类型

Area:函数名

(double radius):函数参数(可选),参数列表由参数类型和参数名(可选)组成,当有多个参数时,用逗号隔开。

​ 函数原型:指出了函数的名称、函数接受的参数列表以及返回值类型

7.1.2 函数定义是什么

​ 函数定义:总是由一个语句块组成。除非返回类型被声明为void,否则必须包含一条return语句。

7.1.3 函数调用和实参是什么

**形参(parameter)**:函数声明中包含的参数。

**实参(argument)**:调用函数提供的参数

7.1.4 编写接收多个参数的函数

123456789101112131415161718192021222324252627282930//程序清单7.2 接受两个参数以计算圆柱表面积的函数#includeusing namespace std;const double Pi = 3.14159265;double SurfaceArea(double radius, double height);int main(){ cout << "Enter the radius of the cyliner:"; double radius = 0; cin >> radius; cout << "Enter the height of the cyliner:"; double height = 0; cin >> height; cout << "SurfaceArea is : " << SurfaceArea(radius, height) << endl; return 0;}double SurfaceArea(double radius, double height){ double area = 2 * Pi * radius * radius + 2 * Pi * radius * height; return area;}

7.1.5 编写没有参数和返回值的函数

1234567891011121314151617//程序清单7.3 没有参数和返回值的函数#includeusing namespace std;void SayHello();int main(){ SayHello(); return 0;}void SayHello(){ cout << "heloo world" << endl;}

7.1.6 带默认值的函数参数

1234567891011121314151617181920212223242526272829303132333435363738//程序清单7.4 计算圆面积的函数,其第二个参数为pi,该参数的默认值为3.14#includeusing namespace std;double Area(double radius, double pi = 3.14);int main(){ cout << "Enter radius: "; double radius = 0; cin >> radius; cout << "pi is 3.14, do you wish to change this(y/n)?"; char changePi = 'n'; cin >> changePi; double circleArea = 0; if (changePi == 'y') { cout << "Enter new Pi: "; double newPi = 3.14; cin >> newPi; circleArea = Area(radius, newPi); } else { circleArea = Area(radius); } cout << "Area is: " << circleArea << endl; return 0;}double Area(double radius, double pi){ return radius * radius * pi;}

7.1.7 递归函数——调用自己的函数

1234567891011121314151617181920//程序清单7.5 使用递归函数计算斐波那契数列中的数字#includeusing namespace std;int GetFibNumber(int fibIndex){ if (fibIndex < 2) return fibIndex; else return GetFibNumber(fibIndex - 1) + GetFibNumber(fibIndex - 2);}int main(){ cout << "Enter 0-Based index of desired Fibonacci Number: "; int index = 0; cin >> index; cout << "Fibonacci number is: " << GetFibNumber(index) << endl; return 0;}

7.2 使用函数处理不同的数据7.2.1 函数重载

​ 名称和返回值相同,但参数不同的函数称为函数重载。

1234567891011121314151617181920212223242526272829303132333435363738394041//程序清单7.7 在同一个函数中使用多条语句#includeusing namespace std;const double Pi = 3.14159265;double Area(double radius);double Area(double radius, double height);int main(){ cout << "Enter z fro cyliner, c for Circle: "; char userSelection = 'z'; cin >> userSelection; cout << "Enter radius: "; double radius = 0; cin >> radius; if (userSelection == 'z'){ cout << "Enter height:"; double height = 0; cin >> height; cout << "Area of cyliner is: " << Area(radius, height) << endl; } else { cout << "Area of cyliner is: " << Area(radius) << endl; } return 0;}double Area(double radius){ return radius * radius * Pi;}double Area(double radius, double height){ return 2 * Area(radius) + 2 * Pi * radius * height;}

7.2.2 将数组传递给函数

示例:void DisplayIntegers(int numbers[], int Length);

第一参数是一个整型数组

第二个参数是数组的长度

12345678910111213141516171819202122232425262728293031323334//程序清单7.8 接受数组作为参数的函数#includeusing namespace std;void DisplayArray(int numbers[], int length);void DisplayArray(char charaters[], int length);int main(){ int myNums[4] = { 24,58,-1,245 }; DisplayArray(myNums, 4); char myStatement[7] = { 'h','e','l','l','o','!','\0' }; DisplayArray(myStatement, 7); return 0;}void DisplayArray(int numbers[], int length){ for (int i = 0; i < length; i++){ cout << numbers[i] << " "; } cout << endl;}void DisplayArray(char charaters[], int length){ for(int i = 0; i < length; i++){ cout << charaters[i] << " "; } cout << endl;}

7.2.3 按引用传递参数

1234567891011121314151617181920212223242526//程序清单7.9 以引用参数(而不是返回值)的方式提供圆的面积#includeusing namespace std;const double Pi = 3.1416;void Area(double radius, double& result);int main(){ cout << "Enter radius: "; double radius = 0; cin >> radius; double areaFetched = 0; Area(radius, areaFetched); cout << "This area is: " << areaFetched << endl; return 0;}void Area(double radius, double& result){ result = radius * radius * Pi;}

7.3 微处理器如何处理函数调用7.3.1 内联函数

123456789101112131415161718//程序清单7.10 将把整数翻倍的函数声明为内联#includeusing namespace std;inline long DoubleNum(int inputNum) { return inputNum * 2;}int main(){ cout << "Enter an integer: "; int input = 0; cin >> input; cout << "Double is:" << DoubleNum(input) << endl; return 0;}

7.3.2 自动推断返回类型

12345678910111213141516171819202122//程序清单7.11 将函数Area()的返回类型指定为auto#includeusing namespace std;const double Pi = 3.14159265;auto Area(double radius){ return Pi * radius * radius;}int main(){ cout << "Enter radius: "; double radius = 0; cin >> radius; cout << "Area is: " << Area(radius) << endl; return 0;}

7.3.3 lambda函数

1234567891011121314151617181920212223242526272829303132//程序清单 7.12 使用lambda函数对数组中的元素进行排序并显示它们#include#include#includeusing namespace std;void DisplayNums(vector& dynArray){ for_each(dynArray.begin(), dynArray.end(), \ [](int Element) {cout << Element << " "; }); cout << endl;}int main(){ vector myNums; myNums.push_back(501); myNums.push_back(-1); myNums.push_back(25); myNums.push_back(-35); DisplayNums(myNums); cout << "Sorting them in descending order" << endl; sort(myNums.begin(), myNums.end(), \ [](int Num1, int Num2) {return (Num1 > Num1); }); DisplayNums(myNums); return 0;}

7.6 作业7.6.1 测验

在函数原型中声明的变量的作用域是什么?

答:作用域仅在函数本身

传递给下述函数的值有何特征?

int Func(int &someNumber);

答:可通过someNumber修改传进来的实参

调用自己的函数叫什么?

答:递归

我声明了两个函数,它们的名称和返回值类型相同,但参数列表不同,这被称为什么?

答:函数重载

栈指针指向栈的顶部、中间还是底部?

答:栈的顶部

7.6.2 练习

编写两个重载函数,它们分别使用下述公式计算球和圆柱体的体积:

答:double Area(double radius);

​ double Area(double radius,double height);

编写一个函数,它将一个double数组作为参数。

答:void DisplayNumbers(double doubleArray,int length);

查错:下述代码有什么错误?

123456789101112131415161718192021222324#includeusing namespace std;const double Pi = 3.1416;void Area(double radius, double result){ result = Pi * radius * radius;}int main(){ cout << "Enter radius: "; double radius = 0; cin >> radius; double areaFetched = 0; Area(radius, areaFetched); cout << "The Area is: " << areaFetched << endl; return 0;}

答:通过使用值传递的方式,用形参修改实参

查错:下述函数声明有什么错误?

double Area(double Pi = 3.14, double radius);

答:函数的默认参数应该从右往左声明

编写一个返回类型为void的函数,在提供了半径的情况下,他能帮助调用者计算圆的周长和面积

答:

123456const double Pi = 3.14;void Calc(double radius, double &area, double &circumference){ area = Pi * radius * radius; circumference = 2 * radius * Pi;}

第八章8.1 什么是指针​ 指针是存储内存地址的变量

8.1.1 声明指针

PointedType * PointerVariableName;

PointedType * PointerVariableName = NULL;

8.1.2 使用引用运算符(&)获取变量地址

​ 如果varName是一个变量,&varName将是存储该变量的地址

1234567891011121314//程序清单8.1 获取int变量和double变量的地址#includeusing namespace std;int main(){ int age = 30; const double Pi = 3.1416; cout << "Integer age is localted at: 0x" << &age << endl; cout << "double Pi is localted at: 0x" << &Pi << endl; return 0;}

8.1.3 使用指针存储地址

​ 要将变量的地址存储到一个指针,需要声明一个同样的指针,并使用引用符号(&)将其初始化为该变量的地址

​ Type* Pointer = &Variable;

12345678910111213//程序清单8.2 声明指针并初始化指针#includeusing namespace std;int main(){ int age = 30; int* pointsToInt = &age; cout << "Integer age is localted at: 0x" << hex << pointsToInt << endl; return 0;}

123456789101112131415161718//程序清单8.3 给指针重新赋值,使其指向另一个变量#includeusing namespace std;int main(){ int age = 30; int* pointsToInt = &age; cout << "pointsToInt points to age now" << endl; cout << "pointaToInt = 0x" << hex << pointsToInt << endl; int dogsAge = 9; pointsToInt = &dogsAge; cout << "pointsToInt points to dogsAge now" << endl; cout << "pointaToInt = 0x" << hex << pointsToInt << endl; return 0;}

8.1.4 使用解除引用运算符(*)访问指向的数据

12345678910111213141516171819202122232425//程序清单8.4 使用解除引用运算符(*)来访问整数值#includeusing namespace std;int main(){ int age = 30; int dogsAge = 9; cout << "Interger age = " << age << endl; cout << "Interger dogsAge = " << hex << dogsAge << endl; int* pointsToInt = &age; cout << "pintersToint points to age" << endl; cout << "pointsToInt = 0x" << hex << pointsToInt << endl; cout << "*pointsToInt = " << dec << *pointsToInt << endl; pointsToInt = &dogsAge; cout << "pintersToint points to dogsAge" << endl; cout << "pointsToInt = 0x" << hex << pointsToInt << endl; cout << "*pointsToInt = " << dec << *pointsToInt << endl; return 0;}

123456789101112131415161718192021//程序清单8.5 使用指针和解除引用运算符操作数据#includeusing namespace std;int main(){ int dogsAge = 30; cout << "Initialized dogsAge = " << dogsAge << endl; int* pointesToAnAGe = &dogsAge; cout << "pointsToAnAge points to dogsAge" << endl; cout << "Enter an age for your dog: "; cin >> *pointesToAnAGe; cout << "Input stored at 0x" << hex << pointesToAnAGe << endl; cout << "Integer dogsAge = " << dec << dogsAge << endl; return 0;}

8.1.5 将sizeof()用于指针的结果

32位:sizeof结果为4

64位:sizeof结果为8

8.2 动态分配内存8.2.1 使用new和delete动态分配内存和释放内存

分配和释放一个元素

12Type* Pointer = new Type;delete Pointer;

分配和释放多个元素

12Type* Pointer = new Tyoe[numElements];delete[] Pointer;

8.2.2 将递增和递减运算符(++和–)用于指针的结果

1234567891011121314151617181920212223242526272829//程序清单8.9 使用偏移量和运算符来递增和递减指针#includeusing namespace std;int main(){ cout << "How many integers you wish to enter?"; int numEntries = 0; cin >> numEntries; int* pointsToInts = new int[numEntries]; cout << "Allocated for " << numEntries << "integers" << endl; for (int counter = 0; counter < numEntries; ++counter) { cout << "Enter number" << counter << ": "; cin >> *(pointsToInts + counter); } cout << "Displaying all numbers entered: " << endl; for (int counter = 0; counter < numEntries; ++counter) { cout << *(pointsToInts++) << " "; } cout << endl; pointsToInts -= numEntries; delete[] pointsToInts; return 0;}

8.2.3 将关键字用于const用于指针

指针包含的地址是常量,不能修改指针指向的数据:

12345int daysInMonrh = 30;int* const pDaysInMonth = &daysInMonrh;*pDaysInMonth = 31;int daysInLunarMonth = 28;pDaysInMonth = &daysInLunarMonth; //error,Cannot change address

指针指向的数据为常量,不能修改,但可以修改指针包含的地址,即指针可以指向其地方

123456int hoursInDay = 24;const int* pointsToInt = &hoursInDay;int monthsInYear = 12;pointsToInt = &monthsInYear;*pointsToInt = 13; //error ,Cannot change data being pointed toint* newPointer = pointsToInt; // error, Cannot assign const to non-const

指针包含的地址以及它指向的值都是常量,不能修改(这种最严格)

12345int hoursInDay = 24;const int* const pHourInDay = &hoursInDay;*pHourInDay = 25; //error, Cannot change data being pointed toint daysInMonth = 30;pHourInDay = &daysInMonth; //error, Cannot change address

8.2.4 将指针传递给函数

12345678910111213141516171819202122232425262728//程序清单8.10 在计算圆面积的函数使用关键字const#includeusing namespace std;void CalcArea(const double* const ptrPi, const double* const ptrRadius, double* const ptrArea) { if (ptrPi && ptrRadius && ptrArea) { *ptrArea = (*ptrPi) * (*ptrRadius) * (*ptrRadius); }}int main(){ const double Pi = 3.1416; cout << "Enter radius of circle: "; double radius = 0; cin >> radius; double area = 0; CalcArea(&Pi, &radius, &area); cout << "Area is = " << area << endl; return 0;}

8.2.5 数组和指针的类似之处

123456789101112131415//程序清单 8.11 数组变量是指向第一个元素的指针#includeusing namespace std;int main(){ int myNumbers[5]; int* pointToNums = myNumbers; cout << "pointToNums = 0x" << hex << pointToNums << endl; cout << "&myNumbers[0] = 0x" << hex << &myNumbers[0] << endl; return 0;}

1234567891011121314151617181920212223242526//程序清单8.12 使用解除引用运算符(*)访问数组中的元素以及将数组运算符([])用于指针#includeusing namespace std;int main(){ const int ARRAY_LEN = 5; int myNumbers[ARRAY_LEN] = { 24,-1,365,-999,2011 }; int* pointToNums = myNumbers; cout << "pointToNums = 0x" << hex << pointToNums << endl; cout << "&myNumbers[0] = 0x" << hex << &myNumbers[0] << endl; cout << "Display array using pointer syntax, operator*" << endl; for (int index = 0; index < ARRAY_LEN; index++) { cout << "Element " << index << " = " << *(myNumbers + index) << endl; } cout << "Display array using ptr with array syntax, operator[]" << endl; for (int index = 0; index < ARRAY_LEN; index++) { cout << "Element " << index << " = " << pointToNums[index] << endl; } return 0;}

8.5 引用是什么​ 引用是变量的别名。声明时,需要将初始化为一个变量

12VarType original = value;VarType ReferenceVarialbe = original;

12345678910111213141516171819//程序清单 8.17 引用是相应变量的别名#includeusing namespace std;int main(){ int original = 30; cout << "original = " << original << endl; cout << "original = " << hex << &original << endl; int& ref1 = original; cout << "ref1 is at address: " << hex << &ref1 << endl; int& ref2 = ref1; cout << "ref2 is at address: " << hex << &ref2 << endl; cout << "Therefore, ref2 = " << dec << ref2 << endl; return 0;}

8.5.1 是什么让引用很有用

​ 可以在函数调用实参时避免复制这个步骤

12345//可避免复制步骤的函数版本类似下面这样:ReturnType DoSomething(Type& parameter);//调用如下ReturnType Result = Dosomething(argument);

1234567891011121314151617181920//程序清单8.18 一个计算平方值通过引用参数返回结果的函数#includeusing namespace std;void GetSquare(int& number){ number *= number;}int main(){ cout << "Enter a number you wish to square: "; int number = 0; cin >> number; GetSquare(number); cout << "Square is: " << number << endl; return 0;}

8.5.2 将关键字const用于引用

​ 须要禁止通过引用修改它的指向变量的值,为此可在声明引用时使用关键字const:

12345int original = 30;const int& constRef = original;constRef = 40; //error: constRef can't change value in originalint& ref = constRef; //error: ref2 is not constconst int& constRef2 = constRef;

按引用向函数传递参数

123456789101112131415161718192021//程序清单8.19 使用const引用确保被调用的函数#includeusing namespace std;void GetSquare(const int& number, int &result){ result = number * number;}int main(){ cout << "Enter a number you wish to square: "; int number = 0; cin >> number; int square = 0; GetSquare(number, square); cout << number << "^2 = " << square << endl; return 0;}

8.5 作业8.8.1 测验

为何不能将const引用赋给非const引用

答:防止非const引用修改const引用的值

new和delete是函数吗?

答:是关键字

指针变量包含的值有何特征?

答:变量地址

要访问指针指向的数据,应使用哪种运算符?

答:解引用运算符(*)

8.8.2 练习

下面的语句显示什么

123456int number = 3;int *pNum1 = &number;*pNum1 = 20;int *PNum2 = pNum1;number *= 2;cout << *pNum2;

答:40

**下面三个重载的函数有何相同和不同之处? **

123int DoSomething(int num1, int num2);int DoSomething(int& num1, int& num2);int DoSomething(int* num1, int* num2);

答:

相同之处

函数名相同

功能相似

返回值相同

不同之处

参数不同

调用方式不同

对参数的影响

使用场景不同

要让练习1中第3行的赋值非法,应如何修改第1行中pNum1的声明(提示:让pNum1不能 修改它指向的数据)?

答:添加const关键字

查错:下面的代码有何错误?

1234567891011#includeusing namespace std;int main(){ int* pointToAnInt = new int; pointToAnInt = 9; cout << "The value at pointToAnInt: " << *pointToAnInt << endl; delete pointToAnInt; return 0;}

答:没有使用解引用运算符(*)就直接对pointToAnInt操作

查错:下面的代码有何错误?

12345678910111213#includeusing namespace std;int main(){ int* pointToAnInt = new int; int * pNumberCopy = pointToAnInt; *pNumberCopy = 30; cout << *pointToAnInt; delete pNumberCopy; delete pointToAnInt; return 0;}

答:对同一块内存重复释放;

修正练习5的代码后,其输出是什么?

答:30

第九章9.1 类和对象9.14 使用指针运算符(->)访问成员

1234567891011121314151617181920212223242526272829303132//程序清单 一个值得编译的Human类#include#includeusing namespace std;class Human {public: string name; int age; void IntroduceSelf() { cout << "I am" + name << " and am " << age << " years old" << endl; }};int main(){ Human firstMan; firstMan.name = "Adam"; firstMan.age = 30; Human firstWoman; firstWoman.name = "Eve"; firstWoman.age = 28; firstMan.IntroduceSelf(); firstWoman.IntroduceSelf(); return 0;}

9.2 关键字public和private9.2.1 使用关键字private实现数据抽象

123456789101112131415161718192021222324252627282930313233343536//程序清单9.2 一个对外隐藏真实年龄并将自己说的更年轻的Human类#include#includeusing namespace std;class Human {public: void SetAge(int inputAge) { age = inputAge; } int GetAge() { if (age > 30) return (age - 2); else return age; }private: int age;};int main(){ Human firstMan; firstMan.SetAge(35); Human firstWoman; firstWoman.SetAge(22); cout << "Age of firstMan " << firstMan.GetAge() << endl; cout << "Age of firstWoman " << firstWoman.GetAge() << endl; return 0;}

9.3 构造函数9.3.1 声明和实现构造函数

​ 构造函数是一种特殊的函数,他与类同名且不返回任何值,Human类的构造函数的声明类似于下面这样

12345class Human{public: Human(); //constuctor declaration};

​ 构造函数可在类声明中实现

12345678class Human{public: Human() { //constructor code here }};

​ 在类声明外定义构造函数

12345678910class Human{public: Human(); //constuctor declaration};Human::Human(){ //constructor code here}

9.3.2 何时及如何使用构造函数

​ 构造函数总是在创建对象时被调用,这让构造函数成为将类成员变量初始化为选定值的理想场所

1234567891011121314151617181920212223242526272829303132333435363738394041424344//9.3 使用构造函数初始化类成员变量#include#includeusing namespace std;class Human {public: Human() { age = 1; cout << "Constructed an instance of class Human" << endl; } void SetName(string humansNmae) { name = humansNmae; } void SetAge(int humansAge) { age = humansAge; } void IntroduceSelf() { cout << "I am " + name << " and am " << age << " years old" << endl; }private: string name; int age;};int main(){ Human firstWoman; firstWoman.SetName("Eve"); firstWoman.SetAge(28); firstWoman.IntroduceSelf(); return 0;}

9.3.3 重载构造函数

​ 与函数一样,构造函数也可以重载

12345678910111213141516171819202122232425262728293031323334//9.4 包含多个构造函数的Human类#include#includeusing namespace std;class Human {public: Human() { age = 1; cout << "Default Constructor: name and age not set" << endl; } Human(string humansName, int humansAge) { name = humansName; age = humansAge; cout << "Overloaded constructor creates "; cout << name << " of " << age << " years " << endl; }private: string name; int age;};int main(){ Human firstMan; Human firstWoman("Eve", 20); return 0;}

9.3.4 没有默认构造函数的类

123456789101112131415161718192021222324252627282930313233343536//9.5 一个有重载的构造函数,但没有默认的构造函数的类#include#includeusing namespace std;class Human {public: Human(string humansName, int humansAge) { name = humansName; age = humansAge; cout << "Overloaded constructor creates "; cout << name << " of " << age << " years " << endl; } void IntroduceSelf() { cout << "I am " + name << " and am" << age << " years old" << endl; }private: string name; int age;};int main(){ Human firstMan("Adam", 25); Human firstWoman("Eve", 20); firstMan.IntroduceSelf(); firstWoman.IntroduceSelf(); return 0;}

9.3.5 带默认值的构造函数参数

​ 就像函数可以带有默认值的参数一样,构造函数也可以

1234567891011121314151617181920class Human {public: Human(string humansName, int humansAge = 25) { name = humansName; age = humansAge; cout << "Overloaded constructor creates "; cout << name << " of " << age << " years " << endl; } void IntroduceSelf() { cout << "I am " + name << " and am" << age << " years old" << endl; }private: string name; int age;};

9.3.6 包含初始化列表的构造函数

123456789101112131415161718192021222324252627//程序清单9.6 接受带默认值的参数的默认构造函数,并使用初始化列表来设置成员#include#includeusing namespace std;class Human {public: Human(string humansName = "Adam", int humansAge = 25) : name(humansName),age(humansAge) { cout << "Overloaded constructor creates "; cout << name << " of " << age << " years " << endl; }private: string name; int age;};int main(){ Human firstMan; Human firstWoman("Eve", 20); return 0;}

​ 也可以使用constexpr关键字将构造函数定义为常量表达式

123456789class Sample{ const char* someString;public: constexpr Sample(const char* input) : someString(input) { //constructor code }};

9.4 析构函数​ 析构函数看起来像一个类同名的函数,但前面有一个腭化符号(~)。因此Human类的析构函数声明如下

12345class Human{public: ~Human(); //declaration of a destructor};

​ 类中声明实现

12345678class Human{public: ~Human() { //destructor code here }};

​ 在类声明外定义

12345678910class Human{public: ~Human(); //declaration of a destructor};Human::~Human(){ //destructor code here}

9.4.2 何时及如何使用析构函数

​ 每当对象不在作用域内或通过delete被删除进而被销毁时,都讲调用析构函数。这使得析构函数称为重置变量以及释放动态分配的内存和其他资源的理想场所

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647//程序清单9.7 一个简单的类,它封装了字符缓冲区并通过析构函数释放它#include#includeusing namespace std;class MyString {public: MyString(const char* initString) { if (initString != NULL) { buffer = new char[strlen(initString) + 1]; strcpy(buffer, initString); } else { buffer = NULL; } } ~MyString() { cout << "Invoking destructor, clearing up" << endl; } int GetLength() { return strlen(buffer); } const char* GetString() { return buffer; }private: char* buffer;};int main(){ MyString sayHello("Hello from String Class"); cout << "String buffer in sayHello is " << sayHello.GetLength() << " characters long" << endl; cout << "Buffer contains: " << sayHello.GetString() << endl; return 0;}

9.5 复制构造函数9.5.1 浅复制及其存在的问题

​ 复制类的对象时,将复制其指针成员,但不复制指针指向的缓冲区,其结果是两个对象指向同一个动态分配的内存。销毁其中的一个对象时,导致另一个对象存储的指针拷贝无效。这种复制被称为浅复制

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950//程序清单9.7 按值传递类的对象带来的问题#include#includeusing namespace std;class MyString {public: MyString(const char* initString) { buffer = NULL; if (initString != NULL) { buffer = new char[strlen(initString) + 1]; strcpy(buffer, initString); } } ~MyString() { cout << "Invoking destructor, clearing up" << endl; delete[] buffer; } int GetLength() { return strlen(buffer); } const char* GetString() { return buffer; }private: char* buffer;};void UseMyString(MyString str){ cout << "String buffer in MyString is " << str.GetLength() << " characters long" << endl; cout << "buffer contains: " << str.GetLength() << endl;}int main(){ MyString sayHello("Hello from String Class"); UseMyString(sayHello); return 0;}

9.5.2 使用复制构造函数确保深复制

​ 复制构造函数是一个重载的构造函数,由编写类的程序员提供。每当对象被复制时,编译器都将调用复制构造函数

123456789class MyString{ MyString(const MyString& copySource);};MyString::MyString(const MyString& copySource){ //Copy constructor implementation code}

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465//程序清单9.9 定义一个复制构造函数,确保对动态分配的缓冲区进行深复制#include#includeusing namespace std;class MyString {public: MyString(const char* initString) { buffer = NULL; cout << "Default constructor: creating new MyString" << endl; if (initString != NULL) { buffer = new char[strlen(initString) + 1]; strcpy(buffer, initString); cout << "buffer points to: 0x" << (unsigned int*)buffer << endl; } } MyString(const MyString& copySource) { buffer = NULL; cout << "Copy constructor: copying from MyString" << endl; if (copySource.buffer != NULL){ buffer = new char[strlen(copySource.buffer) + 1]; strcpy(buffer, copySource.buffer); cout << "buffer points to:0x" << hex << (unsigned int*)buffer << endl; } } ~MyString() { cout << "Invoking destructor, clearing up" << endl; delete[] buffer; } int GetLength() { return strlen(buffer); } const char* GetString() { return buffer; }private: char* buffer;};void UseMyString(MyString str){ cout << "String buffer in MyString is " << str.GetLength() << " characters long" << endl; cout << "buffer contains: " << str.GetLength() << endl;}int main(){ MyString sayHello("Hello from String Class"); UseMyString(sayHello); return 0;}

9.5.3 有助于改善性能的移动构造函数

​ 移动构造函数的语法

12345678MyString(MyString&& moveSource){ if(moveSource.buffer != NULL) { buffer = moveSource.buffer; moveSource.buffer = NULL }}

9.6 构造函数和析构函数的其他用途9.6.1 不允许复制的类

​ 要禁止类对象被复制,可声明一个私有的复制构造函数。确保函数调用无法通过编译。为禁止复制,可声明一个私有的赋值运算符

1234567class President{private: President(const President&); President& operator(const President&); //... other attributes}

9.6.2 只能有一个实列的单例类

​ 要创建单例类,关键字static必不可少

static用于类的数据成员时,该数据成员将在所有实例之间共享

static用于函数中声明的局部变量时,该变量的值将在两次调用之间保持不变

static用于成员函数时,该方法将在所有成员之间共享

123456789101112131415161718192021222324252627282930313233343536373839404142//程序清单9.10 单例类President, 它禁止复制、赋值及创建多个实例#include#includeusing namespace std;class President{private: President() {}; //private default constructor President(const President&); //private copy constructor const President& operator=(const President&);//assignment operator string name;public: static President& GetInstance() { //static objects are constructor only once static President onlyInstance; return onlyInstance; } string GetName() { return name; } void SetName(string InputName) { name = InputName; }};int main(){ President& onlyPresident = President::GetInstance(); onlyPresident.SetName("Abraham Lincoln"); cout << "The name of the President is: " << President::GetInstance().GetName() << endl; return 0;}

9.6.3 禁止在栈中实例化的类

​ 可声明一个私有的析构函数

1234567891011121314151617181920212223242526//程序清单 9.11 数据库类monsterDB,只能使用new在自由存储区创建其对象#includeusing namespace std;class MonsterDB{private: ~MonsterDB() {};public: static void DestroyInstance(MonsterDB* pInstance) { delete pInstance; } void Dosomething(){}};int main(){ MonsterDB* myDB = new MonsterDB(); myDB->DestroyInstance; myDB->DestroyInstance(myDB); return 0;}

9.6.4 使用构造函数进行类型转换

123456789101112131415161718192021222324252627//程序清单 使用explicit避免无意间的隐式转换#includeusing namespace std;class Human{ int age;public: explicit Human(int humansAge) : age(humansAge){}};void DoSomething(Human person){ cout << "Human sent did something" << endl;}int main(){ Human kid(10); Human anotherKid = Human(11); DoSomething(kid); //Human anotherKid2 = 11; //failure: implicit conversion not OK //DoSomething(10); //implicit conversion return 0;}

9.10 声明友元​ 不能从外部访问类的私有数据成员和方法,但这条规则不适用于友元类和友元函数。要声明友元函数或友元类,可使用friend关键字

1234567891011121314151617181920212223242526//程序清单 9.14 使用friend关键字让外部函数DisplayAge()能够访问私有数据成员#include#includeusing namespace std;class Human{ friend void DisplayAge(const Human& person); string name; int age;public: explicit Human(string HumansName,int humansAge) :name(HumansName), age(humansAge){}};void DisplayAge(const Human& person){ cout << person.age << endl;}int main(){ Human firstMan("Adam", 25); cout << "Accessing private member age via friend function: "; DisplayAge(firstMan); return 0;}

12345678910111213141516171819202122232425262728293031//程序清单 9.14 使用friend关键字让外部l类Utility能够访问私有数据成员#include#includeusing namespace std;class Human{ friend class Utility; string name; int age;public: explicit Human(string HumansName,int humansAge) :name(HumansName), age(humansAge){}};class Utility{public: static void DisplayAge(const Human& person) { cout << person.age << endl; }};int main(){ Human firstMan("Adam", 25); cout << "Accessing private member age via friend class: "; Utility::DisplayAge(firstMan); return 0;}

9.15 作业9.15.1 测验

使用new创建类实例时,将在什么地方创建它?

答:堆区

我的类包含一个原始指针int*,它指向一个动态分配的int 数组。请问将sizeof 用于这个类的 对象时,结果是否取决于该动态数组包含的元素数?

答:否

假设有一个类,其所有成员都是私有的,且没有友元类和友元函数。请问谁能访问这些成员?

答:设置成员函数get和set方法

可以在一个类成员方法中调用另一个成员方法吗?

答:可以

构造函数适合做什么?

答:初始化成员变量

构造函数适合做什么?

答:重置变量以及释放动态分配的内存

9.15.2 练习

查错:下面的类声明有什么错误?

1234567Class Human{ int age; string name;public: Human(){}}

答:类声明关键字错误且没有声明完成后加上分号

练习1所示类的用户如何访问成员Human::age?

答:1.使用友元函数进行访问

​ 2.设置成员函数get和set方法

**对练习1中的类进行修改,在构造函数中使用初始化列表对所有参数进行初始化。 **

1234567class Human{ int age; string name;public: Human(string humansName,int humansAge):age{humansAge},name{humansName}{}};

编写一个 Circle 类,它根据实例化时提供的半径计算面积和周长。将 Pi 包含在一个私有成员 常量中,该常量不能在类外访问。

12345678910111213141516171819class Circle{public: Circle(double r = 0) : radius{r}{} double GetArea() { return radius * radius * Pi; } double GetCircumference() { return 2 * radius * Pi; }private: double radius; const double Pi = 3.14;};

第十章10.1 继承基础10.1.2 C++派生语法

​ c++派生语法如下

123456789class Base{ };class Derived : access-specifier Base{ };

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051//程序清单10.1 鱼类世界呈现的一种简单的继承层次结构#includeusing namespace std;class Fish{public: bool isFreshWaterFish; void swim() { if (isFreshWaterFish) { cout << "Swims in lake" << endl; } else { cout << "Swims in sea" << endl; } }};class Tuna : public Fish{public: Tuna() { isFreshWaterFish = false; }};class Carp : public Fish{public: Carp() { isFreshWaterFish = true; }};int main(){ Carp myLunch; Tuna myDinner; cout << "About my food: " << endl; myLunch.swim(); cout << "Dinner" << endl; myDinner.swim(); return 0;}

10.1.3 访问限定符protected

​ 需要让基类的某些属性能在派生类中的访问,但不能在继承层次外部访问,可使用protected关键字

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051//程序清单10.2 一种更好的Fish类设计,可使用关键字protected将成员属性暴露给派生类#includeusing namespace std;class Fish{protected: bool isFreshWaterFish;public: void swim() { if (isFreshWaterFish) { cout << "Swims in lake" << endl; } else { cout << "Swims in sea" << endl; } }};class Tuna : public Fish{public: Tuna() { isFreshWaterFish = false; }};class Carp : public Fish{public: Carp() { isFreshWaterFish = true; }};int main(){ Carp myLunch; Tuna myDinner; cout << "About my food: " << endl; myLunch.swim(); cout << "Dinner" << endl; myDinner.swim(); return 0;}

10.1.4 基类初始化——向基类传递参数

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647//程序清单10.3 包含初始化列表的派生类构造函数#includeusing namespace std;class Fish{protected: bool isFreshWaterFish;public: Fish(bool isFreshWater) : isFreshWaterFish(isFreshWater){} void swim() { if (isFreshWaterFish) { cout << "Swims in lake" << endl; } else { cout << "Swims in sea" << endl; } }};class Tuna : public Fish{public: Tuna() : Fish(false){}};class Carp : public Fish{public: Carp() : Fish(true){}};int main(){ Carp myLunch; Tuna myDinner; cout << "About my food: " << endl; myLunch.swim(); cout << "Dinner" << endl; myDinner.swim(); return 0;}

10.1.5 在派生类中覆盖基类的方法

​ 如果派生类实现了基类的函数,且返回值和特征标相同,相当于覆盖了基类的这个方法,如果使用派生类去调用这个方法,将不在调用基类的方法,而是自己的方法

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657//程序清单10.4 派生类Tuna和Carp覆盖了基类fish的方法Swim#includeusing namespace std;class Fish{private: bool isFreshWaterFish;public: Fish(bool isFreshWater) : isFreshWaterFish(isFreshWater){} void swim() { if (isFreshWaterFish) { cout << "Swims in lake" << endl; } else { cout << "Swims in sea" << endl; } }};class Tuna : public Fish{public: Tuna() : Fish(false){} void Swim() { cout << "Tuna swims real fast" << endl; }};class Carp : public Fish{public: Carp() : Fish(true){} void Swim() { cout << "Carp siwms real slow" << endl; }};int main(){ Carp myLunch; Tuna myDinner; cout << "About my food: " << endl; myLunch.swim(); cout << "Dinner" << endl; myDinner.swim(); return 0;}

10.1.6 调用基类中被覆盖的方法和方法

​ 使用作用域解析运算符(::)

​ myDinner.Fish::Swim();

10.2 私有继承​ 私有继承意味着派生类的实例中,基类的所有公有成员和方法都是私有的——不能从外部访问,即便是Base类的公有成员和方法,也只能被Derived类使用,而无法通过Derived实例来使用它们

1234567891011121314151617181920212223242526272829303132333435363738394041//程序清单10.8 Car类以私有方式继承Motor类#includeusing namespace std;class Motor{public: void SwithcIgnition() { cout << "Ignition ON" << endl; } void PumpFuel() { cout << "Fuel in cylinders" << endl; } void FireCylinders() { cout << "Vroooom" << endl; }};class Car : private Motor{public: void Move() { SwithcIgnition(); PumpFuel(); FireCylinders(); }};int main(){ Car myDreamCar; myDreamCar.Move(); return 0;}

10.3 保护继承​ 保护继承和私有继承的类似之处

它也表示has-a关系;

它也让派生类能够访问基类的所用公有和保护对象

在继承层次结构外面,也不能通过派生类实例访问基类的公有成员

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354//情绪清单10.9 RaceCar类以保护方式继承了Car类,而Car以保护方式继承了Motor类#includeusing namespace std;class Motor{public: void SwithcIgnition() { cout << "Ignition ON" << endl; } void PumpFuel() { cout << "Fuel in cylinders" << endl; } void FireCylinders() { cout << "Vroooom" << endl; }};class Car : protected Motor{public: void Move() { SwithcIgnition(); PumpFuel(); FireCylinders(); }};class RaceCar : protected Car{public: void Move() { SwithcIgnition(); //RaceCar Has access to members of PumpFuel(); //base Motor due to "protected" inheritance FireCylinders(); //between RaceCar & Car, Car & Motor FireCylinders(); FireCylinders(); }};int main(){ RaceCar myDreamCar; myDreamCar.Move(); return 0;}

10.9 作业10.9.1 测试

**我希望基类的某些成员可在派生类中访问,但不能在继承层次结构外访问,该使用哪种访问限定符? **

答:protected

如果一个函数接受一个基类对象作为参数,而我将一个派生类对象作为实参按值传递给它,结果将如何?

答: 会导致切除问题

**该使用私有继承还是组合? **

答:使用组合,这样可以提高设计的灵活性

**在继承层次结构中,关键字using有何用途? **

答:避免隐藏基类方法

Derived 类以私有方式继承了 Base 类,而 SubDerived 类以公有方式继承了 Derived 类。请问 SubDerived 类能访问Base 类的公有成员吗?

答:不能

10.9.2 实践

**创建程序清单10.10所示的Platypus对象时,将以什么样的顺序调用构造函数? **

答:

构造函数:Mammal、Bird、Reptile、Platypus

析构函数:Platypus、Reptile、Bird、Mammal

**使用代码说明Polygon、Triangle和Shape类之间的关系。 **

答:

123class Shape {};class Polygon : public Shape {};class Triangle : public Polygon {};

**D2 类继承了D1类,而D1类继承了Base类。要禁止D2访问Base的公有方法,应使用哪种 访问限定符?在什么地方使用? **

答:应该在D1类继承Base类时使用private关键字

**下面的代码表示哪种继承关系? **

1234class Derived: Base { // ... Derived members };

答:私有继承

**查错:下述代码有何问题? **

12345678class Derived: public Base { // ... Derived members }; void SomeFunc (Base value) { // }

答: 会导致切除问题

第十一章11.1 多态基础11.1.1 为何需要多态行为

12345678910111213141516171819202122232425262728293031323334//程序清单11.1 将Tuna实例传递给Fish参数,并通过参数调用方法#includeusing namespace std;class Fish{public: void Swim() { cout << "Fish swims!" << endl; }};class Tuna : public Fish{public: void Swim() { cout << "Tuna swims" << endl; }};void MakeFishSwim(Fish& inputFish){ inputFish.Swim();}int main(){ Tuna myDinner; myDinner.Swim(); MakeFishSwim(myDinner); return 0;}

11.1.2 使用虚函数实现多态行为

​ 通过使用virtual关键字,可确保编译器调用覆盖版本

123456789class Base{ virtual ReturnType FunctionName(Parameter List);};class Derived{ TeturnType FunctionName(Parameter List);};

123456789101112131415161718192021222324252627282930313233343536373839404142434445//程序清单 11.2 将Fish::Swim()声明为虚函数带来的影响#includeusing namespace std;class Fish{public: virtual void Swim() { cout << "Fish swims!" << endl; }};class Tuna : public Fish{public: void Swim() { cout << "Tuna swims" << endl; }};class Carp : public Fish{public: void Siwm() { cout << "Carp swims!" << endl; }};void MakeFishSwim(Fish& inputFish){ inputFish.Swim();}int main(){ Tuna myDinner; Carp myLunch; MakeFishSwim(myDinner); MakeFishSwim(myLunch); return 0;}

11.1.3 为何需要虚析构函数

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849//程序清单 11.3 在函数中通过基类指针调用运算符delete#includeusing namespace std;class Fish{public: Fish() { cout << "Consturcted Fish" << endl; } ~Fish() { cout << "Destroyed Fish" << endl; }};class Tuna : public Fish{public: Tuna() { cout << "Consturcted Tuna" << endl; } ~Tuna() { cout << "Destroyed Tuna" << endl; }};void DeleteFishMemory(Fish *pFish){ delete pFish;}int main(){ cout << "Allocating a Tuna on The Free store:" << endl; Tuna* pTuna = new Tuna; cout << "Deleteing the Tuna: " << endl; DeleteFishMemory(pTuna); cout << "Instantiating a Tuna on the stack:" << endl; Tuna myDinner; cout << "Automatic destruction as it goes out of scope: " << endl; return 0;}

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950//程序清单11.4 将析构函数声明为虚函数,确保通过基类指针调用delete时,将调用派生类的析构函数//程序清单 11.3 在函数中通过基类指针调用运算符delete#includeusing namespace std;class Fish{public: Fish() { cout << "Consturcted Fish" << endl; } virtual ~Fish() { cout << "Destroyed Fish" << endl; }};class Tuna : public Fish{public: Tuna() { cout << "Consturcted Tuna" << endl; } ~Tuna() { cout << "Destroyed Tuna" << endl; }};void DeleteFishMemory(Fish *pFish){ delete pFish;}int main(){ cout << "Allocating a Tuna on The Free store:" << endl; Tuna* pTuna = new Tuna; cout << "Deleteing the Tuna: " << endl; DeleteFishMemory(pTuna); cout << "Instantiating a Tuna on the stack:" << endl; Tuna myDinner; cout << "Automatic destruction as it goes out of scope: " << endl; return 0;}

11.1.4 虚函数的工作原理——理解虚函数表

1234567891011121314151617181920212223242526//程序清单11.5 对两个相同的类(一个包含虚函数,另一个不包含)进行比较,证明确实存在隐藏的虚函数指针表#includeusing namespace std;class SimpleClass{ int a, b;public: void DoSomething(){}};class Base{ int a, b;public: virtual void DoSomething() {}};int main(){ cout << "sizeof(simpleClass) = " << sizeof(SimpleClass) << endl; cout << "sizeof(Base) = " << sizeof(Base) << endl; return 0;}

11.1.5 抽象基类和纯虚函数

​ 不能实例化的基类被称为抽象基类,这样的基类只有一个用途,那就是从它派生出其他类,要创建抽象基类,可声明纯虚函数

1234567891011121314151617class AbstractBase{public: virtual void DoSomething() = 0;};//该声明告诉编译器,AbstactBase的派生类必须实现方法DoSomething()class Derived : public AbstractBase{public: void DoSomething() { cout << "Implemented virtual function" << endl; }};

12345678910111213141516171819202122232425262728293031323334353637383940414243//程序清单11.6 Fish和Tuan和Crap的抽象基类#includeusing namespace std;class Fish{public: virtual void Swim() = 0;};class Tuna : public Fish{public: void Swim() { cout << "Tuna swims fast in the sea! " << endl; }};class Carp : public Fish{public: void Swim() { cout << "Carp swims slow in the lake!" << endl; }};void MakeFishSwim(Fish& inputFish){ inputFish.Swim();}int main(){ Carp myLunch; Tuna myDinner; MakeFishSwim(myLunch); MakeFishSwim(myDinner); return 0;}

11.2 使用虚继承解决菱形问题1234567891011121314151617181920212223242526272829303132333435//程序清单11.8 在继承层次结构中使用virtual关键字,将基类Animal的实例个数限定为1#includeusing namespace std;class Animal{public: Animal() { cout << "Animal constructor" << endl; } int age;};class Mammal : public virtual Animal {};class Bird : public virtual Animal {};class Reptile : public virtual Animal{};class Platypus final : public Mammal, public Bird, public Reptile{public: Platypus() { cout << "Platpus constructor" << endl; }};int main(){ Platypus duckBilledP; duckBilledP.age = 25; return 0;}

11.3 表面覆盖意图的限定符override​ override会让编译器做如下检查

基类函数是否是虚函数

基类中相应虚函数的特征标是否于派生类中被声明为override的函数完全相同

11.4 使用final来禁止覆盖函数​ 被声明为final的类不能用作基类,同样,对于被声明为final的虚函数,不能在派生类中进行覆盖

11.5 可将复制构造函数声明为虚函数吗​ 不能,因为在基类方法声明中使用了virtual,表示它将被派生的类实现覆盖,这种多态的行为是在运行阶段实现的。而构造函数只能创建固定类型的对象,不具备多态性

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677//程序清单11.9 Tuna和Carp包含Clone函数,它们模拟了虚复制构造函数#includeusing namespace std;class Fish{public: virtual Fish* Clone() = 0; virtual void Swim() = 0; virtual ~Fish() {};};class Tuna : public Fish{public: Fish* Clone() override { return new Tuna (*this); } void Swim() override final { cout << "Tuan swims fast in the sea" << endl; }};class BluefinTuna final : public Tuna{public: Fish* Clone() { return new BluefinTuna(*this); } //Cannot override Tuna::Swim as it is "final" in Tuna};class Carp final : public Fish{ Fish* Clone() override { return new Carp(*this); } void Swim() override final { cout << "Carp swims slow in the lake" << endl; }};int main(){ const int ARRAY_SIZE = 4; Fish* myFishes[ARRAY_SIZE] = { NULL }; myFishes[0] = new Tuna(); myFishes[1] = new Carp(); myFishes[2] = new BluefinTuna(); myFishes[3] = new Carp(); Fish* myNewFishers[ARRAY_SIZE]; for (int i = 0; i < ARRAY_SIZE; i++) { myNewFishers[i] = myFishes[i]->Clone(); } for (int i = 0; i < ARRAY_SIZE; i++) { myNewFishers[i]->Swim(); } for (int i = 0; i < ARRAY_SIZE; i++) { delete myFishes[i]; delete myNewFishers[i]; } return 0;}

11.8 作业11.8 测验

**假设您要模拟形状—圆和三角形,并要求每个形成都必须实现函数Area( )和Print( )。您该怎么办? **

答:把Area和Print声明成纯虚函数

编译器为每个类都创建虚函数表吗?

答:只有在使用了virtual关键字后才会创建

我编写了一个Fish类,它有两个公有方法、一个纯虚函数和几个成员属性。这个类是抽象基类吗?

答:是

11.8.2 练习

**创建一个继承层次结构,实现测验1中的Circle和Triangle类。 **

12345678910111213141516171819202122class Shape{public: virtual double Area() = 0; virtual void Print() = 0; virtual ~Shape() {};};class Circle : public Shape{public: double Area() override{} void Print() override{}};class Triangle : public Shape{public: double Area() override {} void Print() override {}};

**差错:下面的代码有何问题? **

12345678910111213class Vehicle{public: Vehicle(){} ~Vehicle(){}};class Car : public Vehicle{public: Car(){} Car(){} };

答:基类 Vehicle 的析构函数不是虚函数。这可能导致内存泄漏或资源未正确释放的风险。

**给定练习2所示的(错误)代码,像下面这样创建并销毁Car实例时,将按什么样的顺序执行 构造函数和析构函数? **

Vehicle* pMyRacer = new Car;

delete pMyRacer;

答:先调用Vehicle的构造函数,在调用Car的构造函数,delete时在调用Vehicle的析构函数

第十二章12.2 单目运算符​ 12.2.2 单目递增与单目递减运算符

在类中声明单目前缀递增运算符语法

12345Date& operator++(){ //operator implementation code return *this;}

在类中声明单目后缀递增运算符语法

123456789Date& operator++(int){ //store a copy of the current state of the object, before incrementing day Date copy(*this); //increment implementation code //return state before increment(because,postfix) return *this;}

​ 前缀和后缀的语法相似,只是将声明中的++替换成了–

123456789101112131415161718192021222324252627282930313233343536//程序清单12.1 一个处理日、月、年的日历类,可对日期执行递增和递减操作#includeusing namespace std;class Date{public: Date(int inMonth,int inDay,int inYear) : month{inMonth},day{inDay},year{inYear}{} Date& operator++() { ++day; return *this; } Date& operator--() { --day; return *this; } void DisplayDate() { cout << month << " / " << day << " / " << year << endl; }private: int day, month, year;};int main(){ Date holiday(12, 25, 2016); return 0;}

12.2.3 转换运算符

123456789101112131415161718192021222324252627282930313233//程序清单12.2 在Date类中实现转换运算符的简单实现#include#include#includeusing namespace std;class Date{public: Date(int inMonth,int inDay,int inYear) : month{inMonth},day{inDay},year{inYear}{} explicit operator const char* () { ostringstream formattedDate; formattedDate << month << " / " << day << " / " << year; dateInString = formattedDate.str(); return dateInString.c_str(); }private: int day, month, year; string dateInString;};int main(){ Date holiday(12, 25, 2016); cout << "Holiday is on:" << holiday << endl; return 0;}

12.2.4 解除引用运算符(*)和成员选择运算符(->)

123456789101112131415161718192021222324252627282930313233343536#include#include#includeusing namespace std;class Date{public: Date(int inMonth,int inDay,int inYear) : month{inMonth},day{inDay},year{inYear}{} void DisplayDate() { cout << month << "/ " << day << " / " << year << endl; }private: int day, month, year; string dateInString;};int main(){ unique_ptr smartIntPtr(new int); *smartIntPtr = 42; cout << "Integer value is: " << *smartIntPtr << endl; unique_ptr smartHoliday(new Date(12, 25, 2016)); cout << "The new instance of date contains: " << endl; smartHoliday->DisplayDate(); return 0;}

12.3 双目运算符​ 以全局函数或静态成员的方式实现的双目运算符

​ return_type operator_type(parameter1,parameter2);

​ 以类成员的方式实现的双目运算符

​ return_type operator_type(parameter);

12.3.2 双目加法和减法运算符

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849//程序清单12.4 实现了双目加减法日历类#includeusing namespace std;class Date{public: Date(int inMonth,int inDay,int inYear) : month{inMonth},day{inDay},year{inYear}{} void DisplayDate() { cout << month << "/ " << day << " / " << year << endl; } Date operator+(int daysToAdd) { Date newDate(month, day + daysToAdd, year); return newDate; } Date operator-(int daysToAdd) { Date newDate(month, day - daysToAdd, year); return newDate; }private: int day, month, year; string dateInString;};int main(){ Date Holiday(12, 25, 2016); cout << "Holiday on: "; Holiday.DisplayDate(); Date PreviousHoliday(Holiday - 19); cout << "Previous holiday on: "; PreviousHoliday.DisplayDate(); Date NextHoliday(Holiday + 6); cout << "Next Holiday on: "; NextHoliday.DisplayDate(); return 0;}

13.3.3 实现运算符+=与-=

12345678910111213141516171819202122232425262728293031323334353637383940414243444546//程序清单12.5 定义运算符+=和-=,以便将日历向前或向后翻整型输入参数指定天数#includeusing namespace std;class Date{public: Date(int inMonth,int inDay,int inYear) : month{inMonth},day{inDay},year{inYear}{} void DisplayDate() { cout << month << "/ " << day << " / " << year << endl; } void operator+=(int daysToAdd) { day += daysToAdd; } void operator-=(int daysToAdd) { day -= daysToAdd; }private: int day, month, year; string dateInString;};int main(){ Date Holiday(12, 25, 2016); cout << "Holiday is on: "; Holiday.DisplayDate(); cout << "holiday -= 9 gives: "; Holiday -= 9; Holiday.DisplayDate(); cout << "holiday += 25 gives: "; Holiday += 25; Holiday.DisplayDate(); return 0;}

12.3.4 重载等于运算符(==)和不等于运算符(!=)

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758//程序清单12.6 运算符==和!=#includeusing namespace std;class Date{public: Date(int inMonth,int inDay,int inYear) : month{inMonth},day{inDay},year{inYear}{} void DisplayDate() { cout << month << "/ " << day << " / " << year << endl; } bool operator ==(const Date& compareTo) { return ( day == compareTo.day && month == compareTo.month && year == compareTo.year ); } bool operator !=(const Date& compareTo) { return !(*this == compareTo); }private: int day, month, year; string dateInString;};int main(){ Date Holiday1(12, 25, 2016); Date Holiday2(12, 31, 2016); cout << "holiday 1 is: "; Holiday1.DisplayDate(); cout << "holiday 2 is: "; Holiday2.DisplayDate(); if (Holiday1 == Holiday2) { cout << "Equality operator: The tow are on the same day" << endl; } else { cout << "Equality operator: The tow are on the different day" << endl; } if (Holiday1 != Holiday2) { cout << "Equality operator: The tow are on the different day" << endl; } else { cout << "Equality operator: The tow are on the same day" << endl; } return 0;}

12.3.6 重载复制赋值运算符(=)

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960//程序清单12.8 对程序清单9.9的MyString类进行改进,添加了赋值运算符#include#includeusing namespace std;class MyString{public: MyString(const char* initialInput) { if (initialInput != NULL) { buffer = new char[strlen(initialInput) + 1]; strcpy(buffer, initialInput); } else { buffer = NULL; } } MyString& operator=(const MyString& copySource) { if ((this != ©Source) && (copySource.buffer != NULL)) { if (buffer) { delete buffer; } buffer = new char[strlen(copySource.buffer) + 1]; strcpy(buffer, copySource.buffer); } return *this; } operator const char* () { return buffer; } ~MyString() { delete[] buffer; }private: char* buffer;};int main(){ MyString string1("hello"); MyString string2(" World"); cout << "Before assignment: " << endl; cout << string1 << string2 << endl; string2 = string1; cout << "After assignment string2 = string1" << endl; cout << string1 << string2 << endl; return 0;}

12.3.7 下标运算符

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697//程序清单12.9 在MyString类中实现下标运算符,以便随机访问MyString::buffer包含的字符#include#include#includeusing namespace std;class MyString{public: MyString(const char* initialInput) { if (initialInput != NULL) { buffer = new char[strlen(initialInput) + 1]; strcpy(buffer, initialInput); } else { buffer = NULL; } } MyString(const MyString& copySource) { if (copySource.buffer) { buffer = new char[strlen(copySource.buffer) + 1]; strcpy(buffer, copySource.buffer); } else { buffer = NULL; } } MyString& operator=(const MyString& copySource) { if ((this != ©Source) && (copySource.buffer != NULL)) { if (buffer) { delete buffer; } buffer = new char[strlen(copySource.buffer) + 1]; strcpy(buffer, copySource.buffer); } return *this; } const char& operator[](int index) const { if (index < GetLength()) { return buffer[index]; } } int GetLength() const { return strlen(buffer); } operator const char* () { return buffer; } ~MyString() { if(buffer != NULL) delete[] buffer; }private: char* buffer; MyString() {};};int main(){ cout << "Tupe a statement: "; string strInput; getline(cin, strInput); MyString youSaid(strInput.c_str()); cout << "Using operator[] for displaying your input: " << endl; for (int i = 0; i < youSaid.GetLength(); i++) { cout << youSaid[i] << " "; } cout << endl; cout << "Enter index 0 - " << youSaid.GetLength() - 1 << ": "; int index = 0; cin >> index; cout << "Input character at zero-base position: " << index; cout << " is:" << youSaid[index] << endl; return 0;}

12.4 函数运算符operator()​ operator()让对象像函数,被称为函数运算符。函数运算符用于标准模板库(stl),通常stl算法中,其用途包括决策。根据使用的操作数数量,像这样的函数被称为单目谓词和双目谓词

123456789101112131415161718192021//程序清单12.10 一个使用operator()实现的函数对象#include#includeusing namespace std;class Display{public: void operator()(string input) const { cout << input << endl; }};int main(){ Display displayFuncObj; displayFuncObj("Display this string!"); return 0;}

12.5 用于高性能编程的移动构造函数和移动赋值运算符​ 移动构造函数和移动赋值函数运算符乃性能优化功能,属于C++11标准的一部分,皆在避免不必要的临时值(当前语句执行完毕后就不再存在的右值)。对于那些管理动态分配的类,如动态数组类和字符串类

12.5.2 声明移动构造函数和移动赋值运算符

​ 移动构造函数的声明语法如下

12345678910111213141516171819202122232425class Sample{public: Sample(Sample&& moveSource) // Move constructor, note && { ptrResource = moveSource.ptrResource; moveSource.ptrResource = NULL; } Sample& operator=(Sample&& moveSource) //move assignment operator, note && { if (this != &moveSource) { delete[] ptrResource; ptrResource = moveSource.ptrResource; moveSource.ptrResource = NULL; } } Sample(); // default constructor Sample(const Sample& copySource); //copy constructor Sample& operator&(const Sample& copySource); //copy assignmentprivate: Type* ptrResource;};

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125//程序清单 12.11 除复制构造函数和复制赋值运算符除外,还包含移动构造函数和移动赋值运算符的MyString类#include#include#includeusing namespace std;class MyString{public: MyString(const char* initialInput) { if (initialInput != NULL) { buffer = new char[strlen(initialInput) + 1]; strcpy(buffer, initialInput); } else { buffer = NULL; } } MyString(const MyString& copySource) { if (copySource.buffer) { buffer = new char[strlen(copySource.buffer) + 1]; strcpy(buffer, copySource.buffer); } else { buffer = NULL; } } MyString(MyString&& moveSrc) { cout << "Move constructor moves: " << moveSrc.buffer << endl; if (moveSrc.buffer != NULL) { buffer = moveSrc.buffer; moveSrc.buffer = NULL; } } MyString& operator=(const MyString& copySource) { if ((this != ©Source) && (copySource.buffer != NULL)) { if (buffer) { delete buffer; } buffer = new char[strlen(copySource.buffer) + 1]; strcpy(buffer, copySource.buffer); } return *this; } MyString& operator=(MyString&& moveSrc) { cout << "Move assignment op. moves: " << moveSrc.buffer << endl; if (moveSrc.buffer != NULL) { delete buffer; buffer = moveSrc.buffer; moveSrc.buffer = NULL; } return *this; } const char& operator[](int index) const { if (index < GetLength()) { return buffer[index]; } } int GetLength() const { return strlen(buffer); } operator const char* () { return buffer; } MyString operator+(const MyString& addThis) { cout << "operator + called: " << endl; MyString newStr; if (addThis.buffer != NULL) { newStr.buffer = new char[GetLength() + addThis.GetLength() + 1]; strcpy(newStr.buffer, buffer); strcpy(newStr.buffer, addThis.buffer); } return newStr; } ~MyString() { if (buffer != NULL) delete[] buffer; }private: char* buffer; MyString() : buffer{NULL} { cout << "Default constructor called" << endl; }};int main(){ MyString Hello("Hello"); MyString World("World"); MyString CPP(" of C++"); MyString sayHelloAgain("overwrite this"); sayHelloAgain = Hello + World + CPP; return 0;}

12.6 用户自定义的字面值​ 要自定义字面量,可像下面这样定义operator””

1234ReturnType opeator "" YourLiteral(ValueType value){ //conversion code here}

123456789101112131415161718192021222324252627282930//程序清单12.12 将华氏温度和摄氏温度转换为开尔文温度#includeusing namespace std;struct Temperature{ double Kelvin; Temperature(long double kelvin) : Kelvin(kelvin){}};Temperature operator"" _C(long double celcius){ return Temperature(celcius + 273);}Temperature operator"" _F(long double fahrenheit){ return Temperature((fahrenheit + 459.67) * 5 / 9);}int main(){ Temperature k1 = 31.73_F; Temperature k2 = 0.0_C; cout << "k1 is " << k1.Kelvin << " Kelvin" << endl; cout << "k2 is " << k1.Kelvin << " Kelvin" << endl; return 0;}

12.7 作业12.10.1 测验

可以像下面这样,编写两个版本的下标运算符,一个的返回类型为const,另一个为非const吗?

12const Type& operator[](int index); Type& operator[](int index); // is this OK?

答:不可以,C++不允许两个函数的名称相同,返回类型值不同。

**可以将复制构造函数或复制赋值运算符声明为私有的吗? **

答:可以

给Date类实现移动构造函数和移动赋值运算符有意义吗?

答:没有意义,只有在动态分配资源时才会导致复制构造函数和复制赋值函数进行不必要的内存分配和释放

12.10.2 练习

为Date类编写一个转换运算符,将其存储的日期转换为整数。

1234explicit operator int(){ return year*10000 + month * 100 + day;}

*DynIntegers 类以 int私有成员的方式封装了一个动态分配的数组,请给它编写移动构造函数和 移动赋值运算符。 **

12345678910111213141516171819202122232425262728class DynIntegers{private: int* array;public: DynIntegers(DynIntegers&& moveArray) { array = moveArray.array; moveArray.array = NULL; } DynIntegers& operator=(DynIntegers&& moveArray) { if (moveArray.array) { delete[] array; array = moveArray.array; moveArray.array = NULL; } return (*this); } ~DynIntegers() { if (array) { delete[] array; } }};

第十三章 类型转换运算符13.3 C++类型转换运算符​ 4个C++类型转换运算符如下

static_cast

dynamic_cast

reinterpret_cast

const_cast

它们的使用语法相同

1destination_type result = cast_operator(object_to_cast);

13.3.1 使用static_cast

​ static_cast用于相关类型的指针之间进行转换,还可以显示的执行标准数据类型的类型转换——这种转换原本将自动或隐式地进行。用于指针时,static_cast实现了基本地编译阶段检查,确保指针转换为相关类型。使用static_cast可将指针向上转换为基类类型,也可以向下转换为派生类型。

12Base* objBase = new Derived;Derived objDer = static_cast(objBase);

13.3.2 使用dynamic_cast和运行阶段类型识别

​ 顾名思义,与静态类型转换相反,动态类型转换在运行阶段(即应用程序运行时)执行类型转换。可检查dynamic_cast操作的结果,以判断类型转换是否成功

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273//程序清单13.1 使用动态转换判断Fish指针指向的是否时Tuna对象或Carp对象#includeusing namespace std;class Fish{public: virtual void Swim() { cout << "Fish swims in water" << endl; } virtual ~Fish(){}};class Tuna : public Fish{public: void Swim() { cout << "Tuna swims real fast in the sea" << endl; } void BecomeDinner() { cout << "Tuna became dinner in SuShi" << endl; }};class Carp : public Fish{public: void Swim() { cout << "Carp swims real slow in the lake" << endl; } void Talk() { cout << "Carp talked Carp!" << endl; }};void DetectFishType(Fish* objFish){ Tuna* objTuna = dynamic_cast(objFish); if (objTuna) { cout << "Detected Tuna. Making Tuna dinner: " << endl; objTuna->BecomeDinner(); } Carp* objCarp = dynamic_cast(objFish); if (objCarp) { cout << "Detected Carp.Making carp talk: " << endl; objCarp->Talk(); } cout << "Verifying type using virtual Fish::Swim: " << endl; objFish->Swim();}int main(){ Carp myLunch; Tuna myDinner; DetectFishType(&myDinner); cout << endl; DetectFishType(&myLunch); return 0;}

13.3.3 使用reinterpret_cast

​ reinterpret_cast时C++中与C风格类型转换最接近的类型转换运算符,它让程序员能够将一种对象类型转换为另一种,不管它们是否相关。

12Base* objBase = new Base();Unrelated* notRelated = reinterpret_cast(objBase);

13.3.4 使用const_cast

​ const_cast让程序员能够关闭对象的访问修饰符const

13.7 作业13.7.1 测验

您有一个基类对象指针objBase,要确定它指向的是否是Derived1或Derived2对象,应使用哪 种类型转换?

答:dynamic_cast

**假设您有一个指向对象的const引用,并试图通过它调用一个您编写的公有成员函数,但编译 器不允许您这样做,因为该函数不是const成员。您将修改这个函数还是使用const_cast? **

答:修改函数,除非万不得已,否则要使用const_cast

判断对错:仅在不能使用static_cast时才应使用reinterpret_cast,这种类型转换是必须和安全的。

答:对

判断对错:优秀的编译器将自动执行很多基于static_cast的类型转换,尤其是简单数据类型之 间的转换。

答:对

13.7.2 练习

查错:下述代码有何问题?

1234void DoSomething(Base* objBase) { Derived* objDer = dynamic_cast (objBase); objDer->DerivedClassMethod(); }

答:没有判断是否转换成功,就对对象进行了使用

假设有一个Fish指针(objFish),它指向一个Tuna对象,要让一个Tuna指针指向该指针指向的Tuna对象,应使用哪种类型转换?请使用代码证明您的 看法。

12Fish* objFish = new Tuna;Tuna* pTuna = objFish;

123//使用static_castFish* objFish = new Tuna;Tuna* pTuna = static_cast(objFish);

第十四章13.4 模板简介14.4.1 模板声明语法

​ 模板声明以关键字template打头,接下来是类型参数列表。

12template template function / class declaration

关键字template标志着模板声明的开始,接下来是模板参数列表。该参数列表包含关键字typename,它定义了模板参数objType,objType是一个占位符。针对对象实例化模板时,将使用对象的类型替换它。

14.4.2 各种类型的模板声明

​ 模板声明可以是:

函数的声明或实现

类的定义或声明

类模板的成员函数或成员类的声明或定义

类模板的静态数据成员的定义

嵌套在类模板中的类的静态数据成员的定义

类或模板类的成员模板的定义

14.4.3 模板函数

12345678910111213141516171819202122232425262728293031//程序清单14.3 模板函数GetMax,它返回两个参数中较大的一个#include#includeusing namespace std;templateconst Type& GetMax(const Type& value1, const Type& value2){ return value1 > value2 ? value1 : value2;}templatevoid DisplayComparison(const Type& value1, const Type& value2){ cout << "GetMax(" << value1 << ", " << value2 << ") = "; cout << GetMax(value1, value2) << endl;}int main(){ int num1 = -101, num2 = 2011; DisplayComparison(num1, num2); double d1 = 3.14, d2 = 3.1416; DisplayComparison(d1, d2); string name1("Jack"), name2("John"); DisplayComparison(name1, name2); return 0;}

14.4.8 一个模板示例

1234567891011121314151617181920212223242526272829303132333435363738394041//程序清单14.4 包含两个成员属性的模板类#includeusing namespace std;templateclass HoldPair{public: HoldPair(const T1& val1, const T2& val2) : value1(val1), value2(val2) {} const T1& GetFirstValue() const { return value1; } const T2& GetSecondValue() const { return value2; }private: T1 value1; T2 value2;};int main(){ HoldPair<> pairIntDbl(300,10.09); HoldPair pairShortStr(25, "learn templates, love c++"); cout << "The first object contains -" << endl; cout << "Value 1:" << pairIntDbl.GetFirstValue() << endl; cout << "Value 2:" << pairIntDbl.GetSecondValue() << endl; cout << "The second object contains -" << endl; cout << "Value 1:" << pairShortStr.GetFirstValue() << endl; cout << "Value 2:" << pairShortStr.GetSecondValue() << endl; return 0;}

14.4.9 模板的实例化和具体化

实例化:使用一个或多个模板参数来创建特定的类型

具体化:为特定的类型指定行为

123456789101112131415161718192021222324252627282930313233343536373839//程序清单14.5 模板具体化#includeusing namespace std;templateclass HoldPair{public: HoldPair(const T1& val1, const T2& val2) : value1(val1), value2(val2) {} const T1& GetFirstValue() const; const T2& GetSecondValue() const;private: T1 value1; T2 value2;};template<> class HoldPair{public: HoldPair(const int& val1, const int& val2) : value1(val1), value2(val2) {} const int& GetFirstValue() const { cout << "Returning integer " << value1 << endl; return value1; }private: int value1; int value2; string strFun;};int main(){ HoldPair pairInInt(223, 333); pairInInt.GetFirstValue(); return 0;}

14.4.10

​ 模板类的静态成员,由特定的具体化的所用实例共享

12345678910111213141516171819202122232425262728//程序清单14.6 静态成员对模板类和实例的影响#includeusing namespace std;templateclass TestStatic{public: static int staticVal;};template int TestStatic::staticVal;int main(){ TestStatic intInstance; cout << "setting staticVzl for intInstance to 2011" << endl; intInstance.staticVal = 2011; TestStatic dblInstance; cout << "setting staticVal for dblInstance to 1011" << endl; dblInstance.staticVal = 1011; cout << "intInstance.staticVal = " << intInstance.staticVal << endl; cout << "dblInstance.staticVal = " << dblInstance.staticVal << endl; return 0;}

对于模板类的静态成员,通用初始化语法如下:

12template