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