①.Local static Objects静态局部对象
Each local static object is initialized before the first time execution passes through the object’s definition. Local statics are not destroyed when a function ends; they are destroyed when the program terminates.
{
static size_t ctr = 0; // value will persist across calls
return ++ctr;
}
int main()
{
for (size_t i = 0; i != 10; ++i)
cout << count_calls() << endl;
return 0;
}
This program will print the numbers from 1 through 10 inclusive.
Exercise 6.6: Explain the differences between a parameter, a local variable,and a local static variable. Give an example of a function in which eachmight be useful.
local variable: Variables defined inside a block;
parameter: Local variables declared inside the function parameter list
local static variable: local static variable(object) is initialized before the first time execution passes through the object’s definition.Local statics are not destroyed when a function ends; they are destroyed when the program terminates.
size_t count_add(int n) // n is a parameter.
{
static size_t ctr = 0; // ctr is a static variable.
ctr += n;
return ctr;//0+1+2+3+4+5+6+ …
}
int main()
{
for (size_t i = 0; i != 10; ++i) // i is a local variable.
cout << count_add(i) <<",";//程序输出:0,1,3,6,10,15,21,28,36,45,
system("pause");
return 0;
}
Exercise 6.7: Write a function that returns 0 when it is first called and then generates numbers in sequence each time it is called again.当它第一次被调用时返回0,以后每次被调用返回值加1.
size_t generate()
{
static size_t ctr = 0;
return ctr++;
}
注:size_t 类型定义在cstddef头文件中,该文件是C标准库的头文件stddef.h的C++版。它是一个与机器相关的unsigned类型,其大小足以保证存储内存中对象的大小。
例如:bitset的size操作返回bitset对象中二进制位中的个数,返回值类型是size_t。
例如:在用下标访问元素时,vector使用vector::size_type作为下标类型,而数组下标的正确类型则是size_t。vector使用的下标实际也是size_t,源码是typedef size_t size_type。
函数声明也称作函数原型(function prototype).
②Passing Arguments by Reference 传引用参数
void reset(int &i) // i is just another name for the object passed to reset
{
i = 0; // changes the value of the object to which i refers
}
int j = 42;
reset(j); // j is passed by reference; the value in j is changed
cout << "j = " << j << endl; // prints j = 0
在上述调用过程中,形参i仅仅是j的又一个名字。在reset内部对i的使用即是对j的使用。In this call, the parameter i is just another name for j. Any use of i inside reset is a use of j.
③指针形参
// function that takes a pointer and sets the pointed-to value to zero
void reset(int *ip)
{
*ip = 0; // changes the value of the object to which ip points
ip = 0; // changes only the local copy of ip; the argument is unchanged
}
int i = 42;
reset(&i); // changes i but not the address of i
//在C++语言中,建议使用引用类型的形参代替指针:reset(&i);
cout << "i = " << i << endl; // prints i = 0
④Using References to Avoid Copies 避免使用拷贝
It is a somewhat common mistake to define parameters that a function does not change as (plain) references. Doing so gives the function’s caller the misleading impression that the function might change its argument’s value. Moreover, using a reference instead of a reference to const unduly limits the type of arguments that can be used with the function. As we’ve just seen, we cannot pass a const object, or a literal, or an object that requires conversion to a plain reference parameter.
// compare the length of two strings
bool isShorter(const string &s1, const string &s2)
{
return s1.size() < s2.size();
}
⑤string::size_type 类型
无符号类型的值,而且足够存放下任何string对象的大小。
在C++11新标准中,允许编译器通过auto或者decltype来推断变量的类型
auto len=line.size();// line的类型是string::size_type
TIP:如果一条表达式中已经有了size()函数就不要再使用int了,这样可以避免混用int和unsigned可能带来的问题。
⑥关于指针和引用
使用指针交换:
#include <iostream>
#include <string>
#include <stdexcept>
void swap(int* lhs, int* rhs)
{
int tmp;
tmp = *lhs;
*lhs = *rhs;
*rhs = tmp;
}
int main()
{
for (int lft, rht;std::cout << "Please Enter:\n", std::cin >> lft >> rht;)
{ swap(&lft, &rht);
std::cout << lft << " " << rht << std::endl;
}
return 0;
}
void swap(int& lhs, int& rhs)
{
int temp = lhs;
lhs = rhs;
rhs = temp;
}
int main()
{
for (int left, right;
std::cout << "Please Enter:\n", std::cin >> left >> right;) {
swap(left, right);
std::cout << left << " " << right << std::endl;
}
return 0;
}
练习6.13. T是类型名字,说明两个函数的区别:void f(T)、void f(&T).
void f(T) pass the argument by value. nothing the function does to the parameter can affect the argument.
void f(T&) pass a reference, will be bound to whatever T object we pass.
练习6.14. 举一个形参应该是引用的例子,再举一个形参不能是引用类型的例子。
//a parameter should be a reference type:
void reset(int &i)
{
i = 0;
}
//a parameter should not be a reference:
void print(std::vector<int>::iterator begin, std::vector<int>::iterator end)
{
for (std::vector<int>::iterator iter = begin; iter != end; ++iter)
std::cout << *iter << std::endl;
}
练习6.15
#include<iostream>
#include<string>
using std::string;
string::size_type find_char(const string &s, char c, string::size_type &occurs)
{
auto ret = s.size();
occurs = 0;
for (decltype(ret)i = 0; i != s.size(); ++i)
{
if (s[i] == c)
{
if (ret == s.size())//肯定成立,如果出现了c就改变ret的值,之后的迭代就绕过这一句
ret = i;//保证ret只被改变一次
++occurs;
}
}
return ret;//函数返回字母o第一次出现的下标,下标从0开始
}
int main()
{
string s;
string::size_type ctr;
std::cout << "输入字符串\012检索字母o第一次出现的下标,并统计字母o的个数:\012";
while (std::cin >> s)
{
auto index = find_char(s, 'o', ctr);
std::cout <<"o第一次出现在下标:["<<index<<"]。\012o出现次数:"<<ctr<<"。\012\012请重新输入新的字符串:\012";
}
system("pause");
return 0;
}
运行验证:
string::size_type find_char(const string &s, char c, string::size_type &occurs)
//说明find_char函数的三个形参为什么是现在的类型。
//特别说明为什么s是常量引用而occurs是普通引用?why is s a reference to const but occurs is a plain reference?
//cause the s should not be changed by this function. but occurs's result must be calculated by the function.
//为什么s和occurs是引用类型而c不是?Why are these parameters references, but the char parameter c is not?
//It's OK to use const reference here but copying a char directly would be more memory-efficient.
//如果令s是普通引用会发生什么情况?如果令occurs是常量引用会发生什么情况?
//What would happen if we made s a plain reference? What if we made occurs a reference to const?
//s could be changed in the function, and occurs would not be changed. so occurs = 0; is an error.
P191
reset(&i);//调用形参类型是int*的reset函数
reset(i);//调用形参类型是int&的reset函数
练习6.17
#include <iostream>
#include <string>
using std::string;
bool hasUppercase(const string& str)
{
for (auto c : str)
if (isupper(c)) return true;
return false;
}
const string& makeLowercase(string& str)
{
for (auto& c : str)
if (isupper(c)) c = tolower(c);
return str;
}
int main()
{
string str("Hello World!");
std::cout << std::boolalpha << hasUppercase(str) << std::endl;
//boolalpha,函数名称,功能是把bool值显示为true或false。
std::cout << makeLowercase(str) << std::endl;
system("pause");
}
练习6.18:为下面的函数编写声明,从给定的名字中推测函数具备的功能。
(a)名为compare的函数,返回布尔值,两个参数都是matrix类的引用。
bool compare(const matrix &m1, const matrix &m2);
(b)名为change_val的函数,返回vector<int>的迭代器,有两个参数:一个是int,另一个是vector<int>的迭代器。
vector<int>::iterator change_val(int, vector<int>::iterator);
6.2.4. Array Parameters
Arrays have two special properties that affect how we define and use functions that operate on arrays: We cannot copy an array , and when we use an array it is (usually) converted to a pointer (§ 3.5.3, p. 117). Because we cannot copy an array, we cannot pass an array by value. Because arrays are converted to pointers,when we pass an array to a function, we are actually passing a pointer to the array’s first element.
// each function has a single parameter of type const int*
void print(const int*);
void print(const int[]); // shows the intent that the function takes an array
void print(const int[10]); // dimension for documentation purposes (at best)
Regardless of appearances, these declarations are equivalent: Each declares a function with a single parameter of type const int*. When the compiler checks a call to print, it checks only that the argument has type const int*:
int i = 0, j[2] = {0, 1};
print(&i); // ok: &i is int*
print(j); // ok: j is converted to an int* that points to j[0]
Using a Marker to Specify the Extent of an Array
The first approach to managing array arguments requires the array itself to contain an end marker.C-style strings are stored in character arrays in which the last character of the string is followed by a null character. Functions that deal with C-style strings stop processing the array when they see a null character:
void print(const char *cp)
{
if (cp) // if cp is not a null pointer
while (*cp) // so long as the character it points to is not a null character
cout << *cp++; // print the character and advance the pointer
}
the parentheses around *matrix are necessary:
int *matrix[10]; // array of ten pointers 数组
int (*matrix)[10]; // pointer to an array of ten ints 指针
//! Exercise 6.21:
//! Write a function that takes an int and a pointer to an int and
//! returns the larger of the int value or the value to which the
//! pointer points. What type should you use for the pointer?
//!
#include <iostream>
using std::cout;
int LargerOne(const int i, const int* ip)
{
return (i > *ip) ? i : *ip;
}
int main()
{
int c = 6;
cout << LargerOne(7, &c);
return 0;
}
我自己的烂代码,忘记了const:
#include<iostream>
int max(int a, int *b)
{
return a > *b ? a : *b;
}
int main()
{
int a=0, b=0;
int *c=&b;
std::cin >> a >> b;
std::cout<< std::endl;
std::cout << max(a, c);
system("pause");
}
// Exercise 6.22:看不懂 void swap(int*& lft, int*& rht)
// Write a function to swap two int pointers.交换两个int指针。
#include <iostream>
void swap(int*& lft, int*& rht)
{
auto tmp = lft;
lft = rht;
rht = tmp;
}
int main()
{
int i = 42, j = 99;
auto lft = &i;
auto rht = &j;
std::cout << lft << std::endl << rht << std::endl;
swap(lft, rht);
std::cout << lft << std::endl << rht << std::endl;
std::cout << *lft << " " << *rht << std::endl;
system("pause");
return 0;
}
运行显示:
练习6.23.代码汇总了P194管理指针形参的三种常用技术。
#include <iostream>
using std::cout;
using std::endl;
using std::begin;
using std::end;
void print(int* pi)
{
if (pi) cout << *pi << endl;
}
void print(const char* p)
{
if (p)
while (*p) cout << *p++;
cout << endl;
}
void print(const int* beg, const int* end)
{
while (beg != end) cout << *beg++ << endl;
}
void print(const int ia[], size_t size)
{
for (size_t i = 0; i != size; ++i) {
cout << ia[i] << endl;
}
}
void print(const int(&arr)[2])
{
for (auto i : arr) cout << i << endl;
}
int main()
{
int i = 0, j[2] = {0, 1};
char ch[5] = "pezy";
print(ch);
print(begin(j), end(j));
print(&i);
print(j, end(j) - begin(j));
print(const_cast<const int(&)[2]>(j));//看不懂
}
练习6.24;这个题有意思
Explain the behavior of the following function. If there are problems in the code, explain what they are and how you might fix them.
void print(const int ia[10])//不论数组大小是多少,都会输出十个数,错误
{
for (size_t i = 0; i != 10; ++i)
cout << ia[i] << endl;
}
Arrays have two special properties that affect how we define and use functions that operate on arrays:
We cannot copy an array, and when we use an array it is (usually) converted to a pointer.
So we cannot pass an array by value, and when we pass an array to a function, we are actually passing a pointer to the array's first element.
In this question, const int ia[10] is actually same as const int*, and the size of the array is irrelevant. we can pass const int ia[3] or const int ia[255], there are no differences. If we want to pass an array which size is ten, we should use reference like that:
void print10(const int (&ia)[10]) { /*...*/ }
练习6.32
#include<iostream>
int &get(int *arry, int index)
{
return arry[index];
}
int main()
{
int a[10];
for (int i = 0; i != 10; ++i)
{
get(a, i) = i;
std::cout << a[i] << std::endl;//输出0~9
}
system("pause");
}
P202程序
char &get_val(string &str, string::size_type ix)
{
return str[ix]; // get_val assumes the given index is valid
}
int main()
{
string s("a value");
cout << s << endl; // prints a value
get_val(s, 0) = 'A'; // changes s[0] to A
cout << s << endl; // prints A value
return 0;
}
练习6.32: 编写一个递归函数,输出vector对象的内容。
void print(vector<int>::iterator beg, vector<int>::iterator end)
{
if (beg != end)
{
std::cout << *beg << " ";
print(std::next(beg), end);
}
}
int main()
{
vector<int> vec{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
print(vec.begin(), vec.end());
system("pause");
return 0;
}
我自己的代码
void print(vector<int>::iterator beg, vector<int>::iterator end)
{
for (auto i = beg; i != end; ++i)
std::cout << *i << std::endl;
}
练习6.34: 阶乘函数停止条件
if (val!=0 )和if (val>1 )
When the recursion termination condition becomes var != 0, two situations can happen :
case 1 : If the argument is positive, recursion stops at 0.
case 2 : if the argument is negative, recursion would never stop.
As a result,a stack overflow would occur.
练习6.42: 输出success和failure的单复数形式。
#include <iostream>
#include <string>
using std::string;
using std::cout;
using std::endl;
string make_plural(size_t ctr, const string& word, const string& ending = "s")
{
return (ctr > 1) ? word + ending : word;
}
int main()
{
cout << "singual: " << make_plural(1, "success", "es") << " "<< make_plural(1, "failure") << endl;
//输出单数形式
cout << "plural : " << make_plural(2, "success", "es") << " "<< make_plural(2, "failure") << endl;
//输出复数形式
system("pause");
return 0;
}
练习6.33:P205,练习6.47:P217::递归输出vector.
#include<iostream>
#include<vector>
using std::vector;
/*
void print(vector<int>::iterator beg, vector<int>::iterator end)
{
for (auto i = beg; i != end; ++i)
std::cout << *i << std::endl;
}*/
void print(vector<int>::iterator beg, vector<int>::iterator end)
{
if (beg != end)
{
std::cout << *beg << " ";
print(std::next(beg), end);
}
}
void printVec(vector<int>& vec)
{
#ifndef NDEBUG
std::cout<< "vector size: " << vec.size() << std::endl;
#endif
if (!vec.empty())
{
auto tmp = vec.back();
vec.pop_back();
printVec(vec);
std::cout<< tmp << " ";
}
}
int main()
{
vector<int> vec{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
print(vec.begin(), vec.end());
std::cout << std::endl;
printVec(vec);
system("pause");
return 0;
}
运行显示:
练习6.48: 说明下面循环的含义,它对assert的使用合理吗?
string s;
while (cin >> s && s != sought) { } // empty body空函数体
assert(cin);
//This loop let user input a word all the way until the word is sought.
//It isn't a good use of assert. The assert macro is often used to check for conditions that “cannot happen”.
//But the assert would always happen when users input EOF directly. The behavior is very natural, so the check is meaningless.
//using assert(!cin || s == sought) is more better.