Chapter 6. Functions

①.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;
}


使用引用交换The version using reference is easier):

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.



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值