天天看點

C++核心程式設計Day02

1.内聯函數

1.1 内聯函數基本概念
	在c++中,預定義宏的概念是用内聯函數來實作的,而内聯函數本身也是一個真
正的函數。内聯函數具有普通函數的所有行為。唯一不同之處在于内聯函數會在适當
的地方像預定義宏一樣展開,是以不需要函數調用的開銷。是以應該不使用宏,使用
内聯函數。
	在普通函數(非成員函數)函數前面加上inline關鍵字使之成為内聯函數。但是必須注
意必須配合函數體的定義結合在一起,否則編譯器将它作為普通函數來對待。
	内聯函數的确占用空間,但是内聯函數相對于普通函數的優勢隻是省去了函數調用
時候的壓棧,跳轉,傳回的開銷。我們可以了解為内聯函數是以空間換時間。

1.2 類内部的内聯函數
	為了定義内聯函數,通常必須在函數定義前面放一個inline關鍵字。但是在類内部
定義内聯函數時并不是必須的。任何在類内部定義的函數自動成為内聯函數。

1.3 内聯函數和編譯器
	對于任何類型的函數,編譯器會将函數類型(包括函數名字,參數類型,傳回值類型)
放入到符号表中。同樣,當編譯器看到内聯函數,并且對内聯函數體進行分析沒有發現
錯誤時,也會将内聯函數放入符号表。
	當調用一個内聯函數的時候,編譯器首先確定傳入參數類型是正确比對的,或者如果
類型不能完全比對,但是可以将其轉換為正确類型,并且傳回值在目标表達式裡比對正
确類型,或者可以轉換為目标類型,内聯函數就會直接替換函數調用,這就消除了函數
調用的開銷。假如内聯函數是成員函數,對象this指針也會被放入合适位置。
注:
	c++内聯編譯會有一些限制,以下情況編譯器可能考慮不會将函數進行内聯編譯:
	1.不能存在任何形式的循環語句
	2.不能存在過多的條件判斷語句
	3.函數體不能過于龐大
	4.不能對函數進行取址操作
	
	内聯僅僅隻是給編譯器一個建議,編譯器不一定會接受這種建議,如果你沒有将函
數聲明為内聯函數,那麼編譯器也可能将此函數做内聯編譯。一個好的編譯器将會内
聯小的、簡單的函數。
           
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#define ADD(x,y) ((x)+(y))
//内聯函數
inline int Add(int x, int y)
{
	return x + y;
}

void test01()
{
	int a = 10;
	int b = 20;
	cout << ADD(a, b) << endl;
	cout << Add(a, b) << endl;
}

#define MyTEST(x,y) ((x)<(y)?(x):(y))
inline int func2(int x,int y)
{
	return x < y ? x : y;
}

//注:以下操作就是内聯函數和宏函數的差別
void test02()
{
	int a = 1;
	int b = 3;
	//執行操作是 MyTEST(a,b) ((++a)<(b)?(++a):(b)) = 3
	//cout << MyTEST(++a, b) << endl;
	cout << func2(a, b) << endl;
}

int main()
{
	//test01();
	test02();

	system("pause");
	return 0;
}
           

2.函數的參數

2.1 函數的預設參數
	c++在聲明函數原型的時可為一個或者多個參數指定預設(預設)的參數值,當函數
調用的時候如果沒有指定這個值,編譯器會自動用預設值代替,并且,在設定了預設
參數之後,這個參數之後的參數都要提前設定預設值。
2.2 函數的占位參數
	c++在聲明函數時,可以設定占位參數。占位參數隻有參數類型聲明,而沒有參數
名聲明。一般情況下,在函數體内部無法使用占位參數。
           
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

int test01(int a,int b = 10)
{
	return a + b;
}

//注意1:函數的預設參數後面的參數必須都是預設參數
int test02(int a, int b = 10,int c = 0,int d = 10)
{
	return a + b + c + d;
}
//注意2:函數的聲明和實作不能同時有預設參數,即要麼聲明有預設參數,要不實作有預設參數
void test03(int a, int b );
void test03(int a, int b = 0)
{
	cout << "test03調用" << endl;
}

//函數的占位參數,在運算符重載的時候區分前加加和後加加的時候使用
void test04(int a, int = 10)
{
	cout << "test04()的調用" << endl;
}

int main()
{
	//cout << test01(5) << endl;
	//cout << test01(5,5) << endl;
	//test03(10);
	test04(10);
	system("pause");
	return 0;
}
           

3.函數重載

3.1 函數重載基本文法
	實作函數重載的條件:
	1.同一個作用域
	2.參數個數不同
	3.參數類型不同
	4.參數順序不同
注: 函數重載和預設參數一起使用,需要額外注意二義性問題的産生。
           
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;

//函數重載的原理
//在彙編階段,起别名
//例如:int add(int a, int b)别名就是 __addii
//		int add(int a, double b)别名就是 __addid
//		int add(double a,int b)别名就是__adddi
//		int add()别名就是__addv

//函數重載的三種形式(在同一作用域下)
//1.按照函數的參數類型
//2.按照函數的參數個數
//3.按照函數的參數順序
int add(int a, int b)
{
	return a + b;
}

int add(int a, double b)
{
	return a + b;
}

int add(double a,double b)
{
	return a + b;
}

int add(double a,int b)
{
	return a + b;
}

//函數重載和預設參數進行配合使用的時候要注意二義性
void myFunc(int a,int b = 0)
{
	cout << "myFunc(int a,int b = 0)的調用" << endl;
}

void myFunc(int a)
{
	cout << "myFunc(int a)的調用" << endl;
}

void myFunc()
{
	cout << "myFunc()的調用" << endl;
}

int main()
{
	int a = 10;
	double b = 20;

	cout << add(a, a) << endl;
	cout << add(a, b) << endl;
	cout << add(b, b) << endl;
	cout << add(b, a) << endl;

	//myFunc(a);//調用是錯誤的,是因為函數重載後,傳入一個a,函數不知道該調用myFunc(int a),還是該調用"myFunc(int a,int b = 0)
	myFunc(a,a);


	system("pause");
	return 0;
}
           
3.2 函數重載實作原理
	編譯器為了實作函數重載,用不同的參數類型來修飾不同的函數名,比如void func(); 
編譯器可能會将函數名修飾成_func,當編譯器碰到void func(int x),編譯器可能将函數
名修飾為func_int,當編譯器碰到void func(int x,char c),編譯器可能會将函數名修飾為
_func_int_char。
           
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;

//函數重載的原理
//在彙編階段,起别名
//例如:int add(int a, int b)别名就是 __addii
//		int add(int a, double b)别名就是 __addid
//		int add(double a,int b)别名就是__adddi
//		int add()别名就是__addv

//函數重載的三種形式(在同一作用域下)
//1.按照函數的參數類型
//2.按照函數的參數個數
//3.按照函數的參數順序
int add(int a, int b)
{
	return a + b;
}

int add(int a, double b)
{
	return a + b;
}

int add(double a,double b)
{
	return a + b;
}

int add(double a,int b)
{
	return a + b;
}

//函數重載和預設參數進行配合使用的時候要注意二義性
void myFunc(int a,int b = 0)
{
	cout << "myFunc(int a,int b = 0)的調用" << endl;
}

void myFunc(int a)
{
	cout << "myFunc(int a)的調用" << endl;
}

void myFunc()
{
	cout << "myFunc()的調用" << endl;
}

int main()
{
	int a = 10;
	double b = 20;

	cout << add(a, a) << endl;
	cout << add(a, b) << endl;
	cout << add(b, b) << endl;
	cout << add(b, a) << endl;

	//myFunc(a);//調用是錯誤的,是因為函數重載後,傳入一個a,函數不知道該調用myFunc(int a),還是該調用"myFunc(int a,int b = 0)
	myFunc(a,a);


	system("pause");
	return 0;
}
           
3.3 在C++中調用C語言程式注意事項
	在C++中要是調用C語言的函數的話,一般都不能正常編譯通過,之是以會出
現這種問題,就是因為編譯器在函數編譯之後,會将函數名字改為上述的形式,在C++
和C中編寫的函數函數名是不同的,是以當編譯器在C++的環境下使用,C語言編寫的函
數的話,編譯器就無法正确識别所需的函數,是以就需要一些特殊手段。
           
#define _crt_secure_no_warnings
#include<iostream>
#include<string>
using namespace std;

//c++的函數在彙編的時候,會給函數起别名,例如調用void func()函數,就會起名為__z4funcv,
//而c語言在彙編後,給函數的名字會隻有一個下劃線,即_func,是以此時使用c++調用c語言的函數的時候就會報錯


//例如,在一般情況下,我們定義了一個c語言的頭檔案,然後進行聲明
//在c++中調用的時候,應該在c語言的頭檔案(.h檔案中)中,以以下格式進行書寫

/*
#ifdef __cplusplus
extern "c"
{
#endif
	void func();//這是用c寫的代碼檔案,就是告訴編譯器以c的方式進行查找該函數,并起到聲明的作業

#ifdef __cplusplus
}
#endif
*/
int main()
{

	system("pause");
	return 0;
}
           

4. 類和對象

4.1  C和C++中struct差別
	1.c語言struct隻有變量	
	2.c++語言struct 既有變量,也有函數
	
	 struct和class的差別:
		class預設通路權限為private,struct預設通路權限為public.
		
4.2 類的封裝
	1.把變量(屬性)和函數(操作)合成一個整體,封裝在一個類中
	2. 對變量和函數進行通路控制
	通路權限:
		1.在類的内部(作用域範圍内),沒有通路權限之分,所有成員可以互相通路
		2.在類的外部(作用域範圍外),通路權限才有意義:public,private,protected
		在類的外部,隻有public修飾的成員才能被通路,在沒有涉及繼承與派生時,
		private和protected是同等級的,外部不允許通路。
           
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;

//類的封裝
//将屬性和方法封裝到類中

//三種權限
//1.public:類外和類内都可以通路
//2.protected:類外不可以通路,類内可以通路(子類的類内可以通路父類的保護成員)
//3.private:類外不可以通路,類内可以通路

class Maker//類
{
public:
	void set(string name,int id)//成員函數
	{
		this->id = id;
		this->name = name;
	}
	void printMaker()
	{
		cout << "姓名:" << this->name << "\n序号:" << this->id<<endl;
	}
private:
	int id;//成員變量
	string name;
};

void test01()
{
	Maker m;//m是對象
	m.set("張三", 1);
	m.printMaker();
}

int main()
{
	test01();
	system("pause");
	return 0;
}
           
4.3将成員變量設定為private的作用:
1.可賦予用戶端通路資料的一緻性。
	如果成員變量不是public,用戶端唯一能夠通路對象的方法就是通過成員函數。
如果類中所有public權限的成員都是函數,客戶在通路類成員時隻會預設通路函數,
不需要考慮通路的成員需不需要添加()。

2.可細微劃分通路控制。
	使用成員函數可使得我們對變量的控制處理更加精細。如果我們讓所有的成員變
量為public,每個人都可以讀寫它。如果我們設定為private,我們可以實作“不準訪
問”、“隻讀通路”、“讀寫通路”,甚至你可以寫出“隻寫通路”。
           
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;

class Maker//類
{
public:
	void initMaker()//成員函數
	{
		this->name = " ";
		this->age = 0;
	}

	void scanfName(string name)//寫姓名
	{
		this->name = name;
	}

	void printName()//讀姓名
	{
		cout << "姓名:" << this->name << endl;
	}

	void scanfAge(int age)//寫年齡
	{
		if (age >= 0 && age <= 100)
		{
			this->age = age;
			return;
		}
	}
	void printAge()//讀年齡
	{
		cout << "年齡:" << this->age << endl;
	}
private:

	int age;
	string name;
};

void test01()
{
	Maker m;
	m.initMaker();
	m.printName();
	m.printAge();

	m.scanfName("張三");
	m.scanfAge(18);
	m.printName();
	m.printAge();
}


int main()
{
	test01();
	system("pause");
	return 0;
}
           

5.案例

5.1 設計立方體類(Cube),求出立方體的面積( 2*a*b + 2*a*c + 2*b*c )和體積( a * b * c),
分别用全局函數和成員函數判斷兩個立方體是否相等。
           
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;


class Cude
{
public:
	void initCube()
	{
		this->longth = 0;
		this->length = 0;
		this->height = 0;
	}

	void setLongth(int longth)
	{
		this->longth = longth;
	}

	void setLength(int length)
	{
		this->length = length;
	}

	void setHeight(int height)
	{
		this->height = height;
	}

	int squareCube()
	{
		return 2 * (this->height + this->length + this->longth);
	}

	int volumeCube()
	{
		return this->height * this->length * this->longth;
	}

	int comPareCube(Cude c1)
	{
		if (this->longth == c1.longth && this->height == c1.height && this->length == c1.length)
			return 1;
		else
			return 0;
	}
private:
	int longth;
	int length;
	int height;

};

int compareSquareCube(Cude c1,Cude c2)
{
	if (c1.squareCube() == c2.squareCube())
		return 1;
	return 0;
}

int comparevolumeCube(Cude c1, Cude c2)
{
	if (c1.volumeCube() == c2.volumeCube())
		return 1;
	return 0;
}

void test01()
{
	Cude c1;
	Cude c2;
	c1.initCube();
	c1.setHeight(30);
	c1.setLength(20);
	c1.setLongth(10);
	
	c2.initCube();
	c2.setLongth(10);
	c2.setLength(20);
	c2.setHeight(30);

	//成員函數
	if (c1.comPareCube(c2))
		cout << "相等!" << endl;
	else
		cout << "不相等!" << endl;

	//全局函數
	if (compareSquareCube(c1, c2))
		cout << "面積相等!" << endl;
	else
		cout << "面積不相等!" << endl;

	if (comparevolumeCube(c1, c2))
		cout << "體積相等!" << endl;
	else
		cout << "體積不相等!" << endl;
}

int main()
{
	test01();
	system("pause");
	return 0;
}
           
5.2 點和圓的關系
設計一個圓形類(AdvCircle),和一個點類(Point),計算點和圓的關系。
假如圓心坐标為x0, y0, 半徑為r,點的坐标為x1, y1:
	1)點在圓上:(x1-x0)*(x1-x0) + (y1-y0)*(y1-y0) == r*r
	2)點在圓内:(x1-x0)*(x1-x0) + (y1-y0)*(y1-y0) < r*r
	3)點在圓外:(x1-x0)*(x1-x0) + (y1-y0)*(y1-y0) > r*r
           
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;

class Point
{
public:
	void initPoint()
	{
		this->p_x = 0;
		this->p_y = 0;
	}

	void setX(int x)
	{
		p_x = x;
	}

	void setY(int y)
	{
		p_y = y;
	}

	int getX()
	{
		return this->p_x;
	}
	int getY()
	{
		return this->p_y;
	}

private:
	int p_x;
	int p_y;
};

class Circle
{
public:
	void initCircle()
	{
		c_point.setX(0);
		c_point.setY(0);
		c_r = 0;
	}
	void setCircle(int x,int y,int r)
	{
		c_point.setX(x);
		c_point.setY(y);
		c_r = r;
	}
	
	int compareCirPoi(Point p1)
	{
		if (((this->c_point.getX() - p1.getX()) * (this->c_point.getX() - p1.getX()) + (this->c_point.getY() - p1.getY()) * (this->c_point.getY() - p1.getY())) == (this->c_r * this->c_r))
			return 0;

		if (((this->c_point.getX() - p1.getX()) * (this->c_point.getX() - p1.getX()) + (this->c_point.getY() - p1.getY()) * (this->c_point.getY() - p1.getY())) > (this->c_r * this->c_r))
			return 1;
		else
			return -1;
	}

private:
	Point c_point;
	int c_r;
};

void test01()
{
	Point p1;
	Circle c1;
	p1.initPoint();
	p1.setX(10);
	p1.setY(10);

	c1.initCircle();
	c1.setCircle(10, 20, 5);
	if (c1.compareCirPoi(p1) == 0)
		cout << "在圓上!" << endl;
	else if (c1.compareCirPoi(p1) == 1)
		cout << "在圓外!" << endl;
	else
		cout << "在圓内!" << endl;
}

int main()
{
	test01();
	system("pause");
	return 0;
}
           

繼續閱讀