第七章:函數

函數可以看做程序員定義的操作。

講幾個有意思的小函數。

/*** 最大公約數 ****/
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;
}

main:處理命令行選項

主函數參數用來確定程序要執行的操作。

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



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章