1. 函数与数组
? ? ? ?大多数情况下,C++与C语言一样,将数组名视为指针,C++将数组名解释为数组第一个元素的地址,即arr == &arr[0]。但存在一些例外情况:首先,数组声明使用数组名来标记数组存储的位置;其次,将sizeof运算符作用于数组将得到整个数组的长度(单位为字节);再者,对数组名取地址(将地址运算符&作用于数组名)将会得到整个数组的地址,若定义 int cookies[8] = {}; 则&cookies将返回一个32字节内存块的地址(假设int占4字节)。
(1)数组函数使用示例(重点关注使用cin得到坏输入时如何重启输入)
// 房地产
#include <iostream>
using namespace std;
const int Max = 5;
int fill_array(double ar[], int limit);
void show_array(const double ar[], int n);
void revalue(double r, double ar[], int n);
int main() {
double properties[Max];
int size = fill_array(properties, Max);
show_array(properties, size);
if (size > 0) {
cout << "Enter revaluation factor: ";
double factor;
while (!(cin >> factor)) {
// bad input
cin.clear();
while (cin.get() != '\n')
continue;
cout << "Bad input, please enter a number: ";
}
revalue(factor, properties, size);
show_array(properties, size);
}
cout << "Done.\n";
cin.get();
cin.get();
return 0;
}
// 填充数组
int fill_array(double ar[], int limit) {
double temp;
int i;
for (i = 0; i < limit; i++) {
cout << "Enter value #" << i + 1 << ": ";
cin >> temp;
if (!cin) {
// bad input
cin.clear();
while (cin.get() != '\n')
continue;
cout << "Bad input, input process terminated.\n";
break;
}
else if (temp < 0) {
cout << "i = " << i << endl;
break;
}
ar[i] = temp;
}
cout << "i = " << i << endl;
return i;
}
// 显示数组
void show_array(const double ar[], int n) {
for (int i = 0; i < n; i++) {
cout << "Property #" << i + 1 << ": $";
cout << ar[i] << endl;
}
}
// 修改数组
// 将每个元素与同一个重新评估因子相乘
void revalue(double r, double ar[], int n) {
for (int i = 0; i < n; i++)
ar[i] *= r;
}
(2)使用数组区间的函数示例
// 使用数组区间的函数
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
const int ArSize = 8;
int sum_arr(const int* begin, const int* end);
int main() {
int cookies[ArSize] = { 1,2,4,8,16,32,64,128 };
int sum = sum_arr(cookies, cookies + ArSize);
cout << "Total cookies eaten: " << sum << endl;
sum = sum_arr(cookies, cookies + 3);
cout << "First three eaters ate " << sum << " cookies.\n";
sum = sum_arr(cookies + 4, cookies + 8);
cout << "Last four eaters ate " << sum << " cookies.\n";
return 0;
}
int sum_arr(const int* begin, const int* end) {
const int* pt;
int total = 0;
for (pt = begin; pt != end; pt++) {
total += *pt;
}
return total;
}
(3)指针和const
? ? ? 仅当只有一层间接关系(如指针指向基本数据类型)时,才可以将非const地址或指针赋给const指针。如果条件允许,则应当将指针形参声明为指向const的指针(即尽量使用const)。
2. 函数与C风格字符串
(1)将C风格字符串作为参数的函数
// strgfun.cpp -- functions with a string argument
#include <iostream>
using namespace std;
unsigned int c_in_str(const char* str, char ch); //传参是一个字符串(指向字符的指针)
int main() {
char mmm[15] = "minimum"; //可以用字符串常量去初始化一个字符数组
const char* wail = "ululate"; //字符指针
unsigned int ms = c_in_str(mmm, 'm');
unsigned int us = c_in_str(wail, 'u');
cout << ms << " m characters in " << mmm << endl; //注意打印字符串
cout << us << " u characters in " << wail << endl; //注意打印字符串,给cout三种情况都可以打印字符串
return 0;
}
unsigned int c_in_str(const char* str, char ch) {
unsigned int count = 0;
while (*str) {
if (*str == ch)
count++;
str++;
}
return count;
}
(2)返回C-风格字符串的函数
????????函数无法返回一个字符串,但可以返回字符串的地址,这样做的效率更高。
// strgback.cpp -- a function that returns a pointer to char
#include <string>
using namespace std;
char* buildstr(char c, int n); //函数声明--原型
int main() {
int times;
char ch;
cout << "Enter a character: ";
cin >> ch;
cout << "Enter a integer: ";
cin >> times;
char* ps = buildstr(ch, times);
cout << ps << endl;
delete[] ps;
ps = buildstr('+', 20);
cout << ps << "-DONE-" << ps << endl;
delete[] ps;
return 0;
}
char* buildstr(char c, int n) {
char* pstr = new char[n + 1];
pstr[n] = '\0';
while (n-- > 0) {
pstr[n] = c;
}
return pstr;
}
????????上述代码对于内存管理的设计,即让函数返回一个指针,该指针指向new分配的内存,的缺点是,程序员必须记住使用delete。而C++类可以利用构造函数和析构函数处理这些细节。
3. 函数和结构
? ? ? ? 可以将一个结构赋给另外一个结构,同样,也可以按值传递结构,就像普通变量那样,在这种情况下,函数将使用原始结构的副本。另外,函数也可以返回结构。与数组名就是数组第一个元素的地址不同,结构名只是结构的名称,要获得结构的地址,必须使用地址运算符&。
? ? ? ? 使用结构编程时,最直接的方式是像处理基本类型那样来处理结构;也就是说将结构作为参数传递,并在需要时将结构用作返回值使用。但是有个缺点,如果结构非常大,则复制结构将增加内存要求,降低系统运行速度。由于此,很多C语言程序员倾向于传递结构的地址,然后用指针来访问结构内容,C++还可以使用按引用传递的方式。
(1)传递和返回结构(按值传递的例子)
// 将行程时间相加得到小时和分钟的和
#include <iostream>
//#include <fstream>
//#include <string>
struct travel_time {
int hours;
int mins;
};
const int Mins_per_hr = 60;
travel_time sum(travel_time t1, travel_time t2);
void show_time(travel_time t);
int main() {
using namespace std;
travel_time day1 = { 5, 45 }; //如何给结构赋值
travel_time day2 = { 4, 55 };
travel_time trip = sum(day1, day2);
cout << "Two-day total: ";
show_time(trip);
travel_time day3 = { 4, 32 };
cout << "Three-day total: ";
show_time(sum(trip, day3));
return 0;
}
travel_time sum(travel_time t1, travel_time t2) {
travel_time total;
total.mins = (t1.mins + t2.mins) % Mins_per_hr;
total.hours = t1.hours + t2.hours + (t1.mins + t2.mins) / Mins_per_hr;
return total;
}
void show_time(travel_time t) {
using namespace std;
cout << t.hours << " hours, "
<< t.mins << " minutes\n";
}
// 空间坐标系之间的转换
#include <iostream>
#include <cmath> //其中部分函数非常重要
//#include <fstream>
//#include <string>
// 定义表示两种坐标系的结构
struct rect {
double x;
double y;
};
struct polar {
double distance;
double angle;
};
// 函数原型
polar rect_to_polar(rect xypos);
void show_polar(polar dapos);
int main() {
using namespace std;
rect rplace;
polar pplace;
cout << "Enter the x and y values: ";
while (cin >> rplace.x >> rplace.y) {
pplace = rect_to_polar(rplace);
show_polar(pplace);
cout << "Next two numbers (q to quit): ";
}
cout << "Done.\n";
return 0;
}
polar rect_to_polar(rect xypos) {
using namespace std;
polar answer; //按值传递
answer.distance = sqrt(xypos.x * xypos.x + xypos.y * xypos.y); //<cmath>中的函数
answer.angle = atan2(xypos.y, xypos.x);
//atan2()可根据x和y计算角度,另一函数atan()不能区分180度之内和之外的角度,用起来不方便
return answer;
}
void show_polar(polar dapos) {
using namespace std;
const double Rad_to_deg = 57.29577951; //弧度转角度,大约为180/pi,看好实际情况需要哪一种表示形式
// C++库中的数学函数假设角度的单位为弧度,因此应以弧度为单位来测量角度,但为了便于显示,转换为角度值
cout << "distance = " << dapos.distance;
cout << ", angle = " << dapos.angle * Rad_to_deg;
cout << " degrees\n";
}
(2)传递结构的地址
// 空间坐标系之间的转换 -- 传递结构指针
#include <iostream>
#include <cmath> //其中部分函数非常重要
//#include <fstream>
//#include <string>
// 定义表示两种坐标系的结构
struct rect {
double x;
double y;
};
struct polar {
double distance;
double angle;
};
// 函数原型
void rect_to_polar(const rect* pxy, polar* pda);
void show_polar(const polar* pda);
int main() {
using namespace std;
rect rplace;
polar pplace;
cout << "Enter the x and y values: ";
while (cin >> rplace.x >> rplace.y) {
rect_to_polar(&rplace, &pplace); //传递地址
show_polar(&pplace);
cout << "Next two numbers (q to quit): ";
}
cout << "Done.\n";
return 0;
}
void rect_to_polar(const rect* pxy, polar* pda) {
using namespace std;
pda->distance = sqrt(pxy->x * pxy->x + pxy->y * pxy->y);
pda->angle = atan2(pxy->y, pxy->x);
}
void show_polar(const polar* pda) {
using namespace std;
const double Rad_to_deg = 57.29577951;
cout << "distance = " << pda->distance;
cout << ", angle = " << pda->angle * Rad_to_deg;
cout << " degrees\n";
}
4. 函数和string对象
? ? ? ? 与数组相比,string对象与结构更相似。可以将一个对象赋给另一个对象,可以将对象作为完整的实体进行传递。如果需要多个字符串,可以声明一个string对象数组,而不是二维char数组。
// 传递string对象数组
#include <iostream>
//#include <fstream>
#include <string>
using namespace std;
const int SIZE = 5;
void display(const string sa[], int n);
int main() {
string list[SIZE];
cout << "Enter your " << SIZE << " favorite astronomical sights:\n";
for (int i = 0; i < SIZE; i++) {
cout << i + 1 << ": ";
getline(cin, list[i]);
}
cout << "Your list: \n";
display(list, SIZE);
return 0;
}
void display(const string sa[], int n) {
for (int i = 0; i < n; i++) {
cout << i + 1 << ": " << sa[i] << endl;
}
}
????????该程序像对待内置类型(如int)一样对待string对象。
5. 函数与array对象
? ? ? ? 模板array并非只能存储基本数据类型,它还可以存储对象。
// 使用C++11模板类array的例子
#include <iostream>
#include <array>
//#include <fstream>
#include <string>
const int Seasons = 4;
const std::array<std::string, Seasons> Snames = { "Spring", "Summer", "Fall", "Winter" };
// 可以修改array对象的函数
void fill(std::array<double, Seasons>* pa);
// 不可以修改array对象的函数
void show(std::array<double, Seasons> da);
int main() {
std::array<double, Seasons> expenses;
fill(&expenses);
show(expenses);
return 0;
}
void fill(std::array<double, Seasons>* pa) {
using namespace std;
for (int i = 0; i < Seasons; i++) {
cout << "Enter " << Snames[i] << " expenses: ";
cin >> (*pa)[i]; //注意,由于优先级,这里的括号必不可少
}
}
void show(std::array<double, Seasons> da) {
using namespace std;
double total = 0.0;
cout << "\nEXPENSES\n";
for (int i = 0; i < Seasons; i++) {
cout << Snames[i] << ": $" << da[i] << endl;
total += da[i];
}
cout << "Total Expenses: $" << total << endl;
}
6. 函数中的递归
(1)包含一个递归调用的递归
? ? ? ? 注意,每个递归调用都创建自己的一套变量。
// recur.cpp -- using recursion
#include <iostream>
#include <array>
#include <string>
//#include <fstream>
void countdown(int n);
int main() {
countdown(4);
return 0;
}
void countdown(int n) {
using namespace std;
cout << "Counting down ... " << n << " (n at " << &n << ")" << endl;
if (n > 0)
countdown(n - 1);
cout << n << ": Kaboom!" << "(n at " << &n << ")" << endl;
}
(2)包含多个递归调用的递归
? ? ? ? 在需要将一项工作不断分为两项较小的类似的工作时,递归非常有用。例如绘制标尺:标出两端,找到中点并将其标出。然后将同样的操作作用于标尺的左半部分和右半部分。递归方法有时被称为分而治之策略(divide-and-conquer strategy)。
// ruler.cpp -- using recursion to subdivide a ruler
#include <iostream>
#include <array>
#include <string>
//#include <fstream>
const int Len = 66;
const int Divs = 6;
void subdivide(char ar[], int low, int high, int level);
int main() {
using namespace std;
char ruler[Len];
int i;
for (i = 1; i < Len - 2; i++)
ruler[i] = ' ';
ruler[Len - 1] = '\0';
int max = Len - 2;
int min = 0;
ruler[min] = ruler[max] = '|';
cout << ruler << endl;
for (i = 1; i <= Divs; i++) {
subdivide(ruler, min, max, i);
cout << ruler << endl;
for (int j = 1; j < Len - 2; j++)
ruler[j] = ' '; //重置
}
return 0;
}
void subdivide(char ar[], int low, int high, int level) {
if (level == 0)
return;
int mid = (high + low) / 2;
ar[mid] = '|';
subdivide(ar, low, mid, level - 1);
subdivide(ar, mid, high, level - 1);
}
? ? ? ? 如果要求的递归层次很多,这种递归方式时一种糟糕的选择。
7. 函数指针
? ? ? ? 函数也有地址,函数的地址时存储其机器语言代码的内存的起始地址。获取函数的地址只需要使用函数名(后面不跟参数),也就是说,如果think()是一个函数,则think就是该函数的地址。
? ? ? ? 使用函数指针时,比较棘手的是编写原型,而传递地址则非常简单。
// fun_ptr.cpp -- pointers to functions
// 计算代码耗费时间,由函数指针传递不同的计算方式
#include <iostream>
//#include <array>
//#include <string>
//#include <fstream>
double betsy(int);
double pam(int);
void estimate(int lines, double (*pt)(int));
int main() {
using namespace std;
int code;
cout << "How many lines of code do you need? ";
cin >> code;
cout << "Here's Betsy's estimate:\n";
estimate(code, betsy);
cout << "Here's Pam's estimate:\n";
estimate(code, pam);
return 0;
}
double betsy(int lns) {
return 0.05 * lns;
}
double pam(int lns) {
return 0.03 * lns + 0.0004 * lns * lns;
}
void estimate(int lines, double(*pf)(int)) {
using namespace std;
cout << lines << " lines will take ";
cout << (*pf)(lines) << " hour(s)\n";
}
|