函数可以看做程序员定义的操作。
讲几个有意思的小函数。
/*** 最大公约数 ****/
int gcd(int x1,int x2)
{
while(x2)
{
int temp=x2;
x2=x1%x2;
x1=temp;
}
return x1;
}
非引用形参:
1.void f(const int *ip); // 可以用 int*类型, const int* 类型实参调用。
void f(int *ip); // 只可以用 int*类型实参调用(想想就明白了)
回忆:可以用 const对象指针初始化为指向非 const对象,反之则不可以。
2.
void fcn(const int i) { /* fcn can read but not write to i */ }
void fcn(int i) { /* .....*/ } // error: redefines fcn(int)
引用形参:
使用 const 引用可以避免修改复制。如果使用形参的不修改实参,应将形参定义为 const 引用,更灵活。
非 const 形参只能与完全同类型的非 const 对象关联。
vector 和其他容器类型形参:避免复制,用迭代器来传递容器。
数组形参:
void f(int a[n]) // n 被忽视
void f(int *a)
void f(int a[])
三者等价。
void f(int (&a)[n]) // n 被考虑 OK: arr is a reference to an arr of 10 ints :不会将数组实参转化为指针,而是传递数组的引用本身。传递数组名
void f(int &a[]) // error: a is an array of references
多维数组:
int *matrix[10]; // array of 10 points
int (*matrix)[10]; // pointer to an array of 10 ints
void f(int (*matrix)[10],int rowsize);//除第一维以外,所有维长度都是元素类型一部分
void f(int matrix[][10],int rowSize);// 同一维数组,编译器忽略第一维长度,所以最好不放在形参表内;
try coding:
#include<iostream>
using namespace std;
void f1(int (*matrix)[10],int rowSize);
void f2(int matrix[][10],int rowSize);
void f1(int (*matrix)[10],int rowSize)
{
int cnt=0;
for(int i=0;i<rowSize;++i)
for(int j=0;j<10;++j)
{
cnt++;
cout<<matrix[i][j]<<" ";
if(cnt>=10)
{
cnt%=cnt;
cout<<endl;
}
}
}
void f2(int matrix[][10],int rowSize)
{
int cnt=0;
for(int i=0;i<rowSize;++i)
for(int j=0;j<10;++j)
{
cnt++;
cout<<matrix[i][j]<<" ";
if(cnt>=10)
{
cnt%=cnt;
cout<<endl;
}
}
cout<<endl;
}
int main()
{
int arr[5][10]={};
int val=0;
for(int i=4;i>=0;--i) // 不能用size_t
for(int j=9;j>=0;--j)
arr[i][j]=val++;
f1(arr,5);
f2(arr,5);
return 0;
}
主函数参数用来确定程序要执行的操作。
int main( int argc,int **argv ); // argv 是指向 char* 的指针。
argv[i] 是 C 风格字符串数组。
argc 表示传递该数组中字符串个数。
/***** 文件名:chapter7.cpp *******/
#include<iostream>
using namespace std;
int main(int argc,char**argv)
{
cout<<"argc = "<<argc<<endl;
for(int i=0;i<argc;++i)
cout<<"argv["<<i<<"] = "<<argv[i]<<endl;
system("pause");
return 0;
}
生成可执行文件移至 C: 目录下
输入:prog -d -o ofile data0 (prog who what how end)
argv[0]总是被设置为当前正在被调用的命令(程序运行生成的exe文件名)。从索引 1 到argc-1表示被传递给命令的实际选项。
举例取出在argv中的命令行选项。程序将支持下列用法:program_name [-d] [-h] [-v] [-o output_file] [-l limit_value] file_name [file_name [file_name [...]]]。
加括号的内容是可选项。
/********* C++ 处理 main() 函数命令行 *********/
#include <iostream>
#include <vector>
#include <string>
#include<cstdlib>//atoi()
using namespace std;
const char* const prog_name = "chapter7";
const char* const prog_version = "version 1.0 (2012/5/12)";
//退出函数
inline void usage(int exit_value = 0)
{
cerr<<prog_name<<" usage!"<<endl;
exit(exit_value);
}
int main(int argc,char* argv[])
{
//设置标识记录命令选项
bool debug_on = false;
bool ofile_on = false;
bool limit_on = false;
string ofile_name;//记录出现的输出文件名
int limit = -1;
vector <string> file_names;//记录文件名
cout<<"argc:"<<argc<<endl;
for(int i = 1;i < argc; ++i)
{
cout<<"argv["<<i<<"]:"<<argv[i]<<endl;
char *pchar = argv[i];
switch(pchar[0]) //确定选项类型:-h,-d,-v,-l,-o;或者其他
{
case '-':
{
cout<<"-"<<endl;
switch(pchar[1]) // options:h,d,v,l,o
{
case 'd':
cout<<"debug"<<endl;
debug_on = true;
break;
case 'v':
cout<<"version"<<endl;
cout<<prog_name<<":"<<prog_version<<endl;
return 0;
case 'h':
cout<<"help"<<endl;
usage();
case 'o':
cout<<"output file!"<<endl;
ofile_on = true;
break;
case 'l':
cout<<"resource limit!"<<endl;
limit_on = true;
break;
default:
cerr<<prog_name<<": error:unrecognized option:"<<pchar<<endl;
usage(-1);
}
break;
}
default:// not start by "-",filename
if(ofile_on)
{
cout<<"filename:"<<pchar<<endl;
ofile_name = pchar;
ofile_on = false;
}
else if(limit_on) // limit value
{
limit_on = false;
limit = atoi(pchar);
if(limit<0)
{
cerr<<prog_name<<":error:negative value for limit!"<<endl;
usage(-2);
}
}
else
file_names.push_back(pchar); // filename
break;
}
}
if(file_names.empty())
{
cerr<<prog_name<<":error:no file for processing!"<<endl;
usage(3);
}
else
{
cout<<(file_names.size() == 1 ? "File":"Files")<<
" to be processed:"<<endl;
for(vector<int>::size_type i = 0;i < file_names.size();++i)
{
cout<<file_names[i]<<"\t"<<endl;
}
}
if(limit != -1)
{
cout<<"user-specified limit(-1):"<<limit<<endl;
}
if(!ofile_name.empty())
{
cout<<"user-specified ofile(empty):"<<ofile_name<<endl;
}
}
含有可变形参的函数:
C++中的省略符形参时为了编译使用了 varargs 的 C 语言程序。对于 C++ 程序,只能将简单数据类型传递给含有省略符形参的函数,大多数类类型都不能正确的复制。
两种形式:(省略符暂停类型检查机制)
void foo(parm_list,...); //逗号为可选项.最常用
void foo(...);
return 语句:
#include<cstdlib>; //定义两个预处理变量:EXIT_FAILURE, EXIT_SUCCESS,可使main 返回值独立与机器
int main()
{
if(some_failure)
return EXIT_FAILURE;
else
return EXIT_SUCCESS;
}
返回非引用类型时,返回值时创建临时对象,然后复制临时对象值来进行初始化。
返回引用类型时,直接赋值,不复制。
不能返回对局部变量的引用(或指针):
/********* 返回引用的函数返回一个左值 ***********/
const string &manip(const string &s)
{
string ret=s;
return ret; // runtime error: return local reference to a local object
}
确保安全方法:问,这个引用指向在此之前存在的哪个对象?
默认实参:
string screenInit(string::size_type height=24,string::size_type wideth=80,char background=' ');//前有默认实参,其后必有。默认实参只能指定一次
screen=screenInit(,,'?'); // error:若后赋值,其前必赋值
string screenInit(string::size_type =24,string::size_type =80,char ='*');
string screenInit(string::size_type h,string::size_type w,char b)
{
for(int i=0;i<h;++i)
{
for(int j=0;j<w;++j)
cout<<b;
cout<<endl;
}
return "";
}
int main()
{
string screen=screenInit();
screen=screenInit(66);
return 0;
}
静态局部对象:
size_t count_calls()
{
static size_t cnt=0;
return ++cnt;
}
int main()
{
size_t i=1;
while((++i)!=10)
cout<<count_calls()<<endl;
return 0;
}
内联函数:
大多数编译器不支持递归函数的内联。不支持太大的内联。
*定义在头文件,不能只有函数原型。
编译器隐式地将在类内定义的成员函数当做内联函数。(定义即类内实现)
类的成员函数:
class Sales_item {
public:
// operations on Sales_item objects
double avg_price() const;
bool same_isbn(const Sales_item &rhs) const
{ return isbn == rhs.isbn; }
// default constructor needed to initialize members of built-in type
Sales_item(): units_sold(0), revenue(0.0) { }
private:
std::string isbn;
unsigned units_sold;
double revenue;
};
每个成员函数(除了 static 成员函数外)都有一个额外隐含的形参
this.
total.same_isbn(trans); //{ isbn==trans.sibn}
类似于
Sales_item::same_isbn(&total,trans); // same_isbn 中数据成员 isbn 属于对象 total.
const 成员函数:
const 改变了隐含的 this 形参的类型。const说明:
1.const 成员函数不能调用非 const 成员函数,不能修改成员变量。
2.const 对象,指向 const 对象的指针或引用不能调动非 const 成员函数。
3.const 成员函数,完全不同于返回值为 const 的函数。
重载:枚举类型的参数匹配:整数对象即使具有与枚举元素相同值也不能作为实参来调用以枚举类型为参数的函数。
枚举类型对象的初始化:一个枚举成员,或者同一枚举类型的另一个对象,进行初始化。
enum Tokens {INLINE,VIRTUAL=128};
void ff(Tokens);
void ff(int);
int main()
{
Tokens curTok=INLINE;
ff(0); // ff(int), not ff(Tokens)
ff(INLINE);// ff(Tokens)
ff(curTok);// 同上,ff(Tokens)
return 0;
}
函数指针:
bool (*pf) (const string &, const string &); // 声明(定义)一个函数指针 pf
typedef bool (*cmpFcn) (const string &, const string &); // 定义指针类型同义词 cmpFcn. 或者说定义一种函数指针类型的名字为 cmpFcn
初始化或赋值:只能通过同类型的函数,或函数指针,或0值常量表达式完成。
调用函数可不需要解引用操作符:
cmpFcn pf=[&]Special_function;
pf("hi", "bye"); 等价于 (*pf) ("hi", "bye");
函数指针形参:即函数的形参是可以指向函数的指针。
void useBigger(const string &, const string &, bool [(*)] (const string &, const string &));
*返回指向函数的指针:参考:http://www.cnblogs.com/AnnieKim/archive/2011/11/20/2255813.html
/* 函数ff(int)返回类型为函数指针 */
int (*ff(int)) (int *,int);
/* 等价于 */
typedef int (*PF)(int*,int);
PF ff(int);
注意:函数类型形参所对应的的实参可以自动转换为指向相应函数类型的指针;但返回函数时则无法实现,故返回类型只能是函数指针,而不能是函数。
typedef int func(int*,int);
void f1(func); // ok
func f2(int); // error
func *f3(int); // ok
"《C++ primer 3》中有指出,指向C函数的指针和指向C++函数的指针类型不同,但是现在的很多编译器都有语言扩展,认为这两种函数的指针具有相同的特性."
#include<iostream>
#include<string>
using namespace std;
extern "C" int InsideFunctionC(const string &s1, const string &s2)
{
return s1.compare(s2);
}
int InsideFunctionCPlusPlus(const string &s1,const string &s2)
{
return s1.compare(s2);
}
extern "C" void OutsideFunction(int (*fc)(const string &,const string &))
{
cout<<fc("great","greet")<<endl;
}
int main()
{
int (*test)(const string &, const string &)=InsideFunctionC; // C++函数指针赋值C函数指针
OutsideFunction(InsideFunctionC);
OutsideFunction(InsideFunctionCPlusPlus);
return 0;
}