從C語言到C++

面向對象特徵:
抽象,多態,繼承,封裝

1.條件編譯

#if 0       #if 1     #ifdef    #ifndef
...         ...			...		...
#endif      #else	  #endif	#else
	       ...					...
            #endif 				#endif
***********/

2.輸入輸出流,名字空間

using namespace std;
using std::cin;
std::cin>>i;

3.訪問和內部作用域變量同名的全局變量,要用全局作用域限定 ::

#include <iostream> 
using namespace std;
double a = 128;
int main (){
   double a = 256;
   cout << "Global a: " <<::a << endl;  //::是全局作用域限定    
   return 0;
}

4. 通過 try-catch處理異常情況 :正常代碼放在try塊,catch中捕獲try塊拋出的異常

     try {                        //放置正常代碼,但可能出現問題
      if (a > 100) throw 100;   //throw 拋出異常(也稱爲拋棄異常),100爲異常對象,出現異常則下面的語句不會執行
      if (a < 10)  throw 10;    //throw後下面語句便不執行了
      throw "hello";
   }
   catch (int result) {         //捕獲異常
    ........
   }
   catch (char * s) {            //捕獲字符串
	 ........
   }

5. 引用(別名),引用主要用於函數形參,C語言函數形參都是值參數

int a = 3, &r = a;   //r就是a
void swap(int &x, int &y) {  //利用別名,形參x就是實參a
	int t = x;
	x = y;
	y = t;
}

6.默認形參

void print(char ch, int n = 1) {   //n爲默認形參,默認形參必須在右邊。n若不傳遞參數就爲1,傳遞則是傳遞的數
	for (int i = 0; i < n; i++)
		cout << ch;
}

7 函數重載 :允許有同名函數存在,只要形參不同,注意:不能根據返回類型區分同名函數

函數重載,函數重寫,同名隱藏
https://blog.csdn.net/inter_peng/article/details/53940179
https://blog.csdn.net/u014725884/article/details/47213651

8函數模板,類模板

template<typename T>   //函數模板,  typename 也可寫成class
T add(T x, T y) {
	return x + y;
}
int main() {
// cout << add<int>(5, 3) << endl; 
	cout << add(5.3, 7.8) << endl;
	cout << add((double)5, 7.8) << endl; //歧義性   ,//函數模板是不允許隱式類型轉換的,調用時類型必須嚴格匹配
}
//類模板
template<class T>
class Array {
	T size;
	T *data;
public:
	Array(int s) {   //構造函數
		size = s;
		data = new T[s];
	}
	virtual ~Array() {
		delete[] data;
	}
};

9.string,vector,迭代器iterator

string類具體操作: https://www.cnblogs.com/X-Do-Better/p/8628492.html

//vector
#include <iostream> 
#include <vector>    // vector是一個類模板
using namespace std;
int main() {	
	vector<int> v = { 7, 5, 16, 8 }; 
	  vector<int >  V2;
	v.push_back(25);    //push_back(),最後添加一個元素  	//成員函數size()、下標運算符[]   
    int	 i = v.size(); 
	v.pop_back();    //刪除最後元素
	v.resize(2);

}
string::const_iterator cii;    //const迭代器,不能修改,  iterator能修改  string::iterator cii;

10.new/delete

int *p = new int;              delete p;   //防止內存泄漏
int *p = new int[n];			delete[] p; 

11. this指針: 指向類對象自己,成員函數實際上隱含一個this指針

#include <iostream> 
#include <string> 
using namespace std;

struct student {
	string name;	   	double score;
	void print() {
		cout << this->name << " " << this->score << endl;
	}
};
int main() {
	student stu;
	stu.name = "Li Ping";	   stu.score = 78.5;
	stu.print();   // print(&stu);   
}

12.訪問控制、類接口

struct和class區別: struct裏的成員默認是public(公開的) class裏的成員默認是private(私有的)
接口:public的公開成員(一般是成員函數)稱爲這個類的對外接口,外部函數只能通過這些接口訪問類對象,
private等非public的包含內部內部細節,不對外公開,從而可以封裝保護類對象!

13.構造函數 ,拷貝構造函數,析構函數

構造函數是和類名同名且沒有返回類型的函數,在定義對象時會自動被調用,而不需要在單獨調用專門的初始化函數如init
構造函數用於初始化類對象成員,包括申請一些資源,如分配內存、打開某文件等
如果不寫構造函數,編譯器會默認生成一個String(){ }

析構函數是在類對象銷燬時被自動調用,用於釋放該
爲了防止內存泄漏的發生,最好將基類的析構函數寫成virtual虛析構函數。

默認的“拷貝構造函數”是“硬拷貝”或“逐成員拷貝”,指針指向同一塊內存區域,當多次釋放同一塊內存就出錯了!

#include <iostream> 
using namespace std;
class String {
	char *data; //C風格的字符串 
	int n;    //字符個數
public:
	~String() {
	..........
		if(data)
			delete[] data;
	}

	String(const String &s) {   // 重定義拷貝構造函數  硬拷貝{data=s.data;	s=s.n},
	                             //執行默認拷貝構造函數時s3和str2指向同一個地址,改變s3那麼str2也改變
		cout << "拷貝構造函數!\n";	
               //當需要申請資源時,後需要將資源釋放掉,最好重新定義拷貝構造函數,防止析構時多次釋放
		data = new char[s.n + 1];		
		........
	}

	String(const char *s=0) {   //構造函數 ,:函數名和類名相同且無返回類型的成員函數
	. . . . .
		}
  ............
};

ostream& operator<<(ostream &o, String s) {
	for (int i = 0; i < s.size(); i++)
		cout << s[i];
	return o;
}

void f() {
	String str,str2("hello world");
	str2[1] = 'E';
//	cout << str2 << endl; 

	String s3 = str2; 			//拷貝構造函數   執行默認拷貝構造函數時s3和str2指向同一個地址,改變s3那麼str2也改變
	cout << str2 << endl;  //str2傳遞給s調用ostream& operator<<(ostream &o, String s) 時 會默認調用拷貝構造函數
                           //調用時s和str2指向同一個地址,結束後調用析構函數s銷燬,
						   //當f函數結束時,str2會銷燬,造成了同一個內存多次銷燬   所以要自定義拷貝構造函數 
}

14.運算符重載:針對用戶定義類型重新定義運算符函數

運算符與普通函數在調用時的不同之處是:對於普通函數,參數出現在圓括號內;而對於運算符,參數出現在其左、右側。

Complex a, b, c;   
…  
c = Add(a, b); // 用普通函數  
c = a + b;   // 用運算符+   
class Point{
	double x, y;
public:	
	double operator[](int i) const{ 	//const函數   。無法給通過下標賦值,爲了和下面不一樣。加了const,返回x複製品
		if (i == 0) 		return x;
		else if (i == 1)    return y;
		else throw "下標非法!"; 	//拋出異常     
	}
	double& operator[](int i) {   //下標可修改,返回x本身
		if (i == 0) return x;
		else if (i == 1) return y;
		else throw "下標非法!"; 	//拋出異常     
	}
	Point(double x_,double y_)       //構造函數
		x = x_; y = y_;
	}
	Point operator+(const Point q) {    //作爲類的內部函數第一個參數就是調用操作本身。
		return Point(this->x+q[0],this->y + q[1]);
	}			
	friend ostream & operator<<(ostream &o, Point p);    //友元函數   
	friend istream & operator>>(istream &i, Point &p);
};

ostream & operator<<(ostream &o, Point p) {
	o <<p.x << " " << p.y<< endl;
	return o;
}
istream & operator>>(istream &i, Point &p) {
	i >> p.x >> p.y;
	return i;
}
/* Point operator+(const Point p,const Point q) { return Point(p[0] + q[0], p[1] + q[1]); } */

int main() {
	Point p(3.5, 4.8),q(2.0,3.0);	 //自動調用構造函數
	cout << p;
	cout << p[0] << "-" << p[1] << endl; //p.operator[](0)   
	p[0] = 3.45; p[1] = 5.67;
	cout << p<<q;	
	Point s = p + q; //p.operator+(q) 內部調用vs operator+(p,q)外部調用
	cout << s;
}

友元函數:爲了使其他類的成員函數直接訪問該類的私有變量。即:允許外面的類或函數去訪問類的私有變量和保護變量,從而使兩個類共享同一函數。
實際上具體大概有下面兩種情況需要使用友元函數:(1)運算符重載的某些場合需要使用友元。(2)兩個類要共享數據的時候

15. Inheritance繼承(Derivation派生): 一個派生類(derived class)

多重繼承: 從一個類派生出多個不同的類
多重派生: 從多個不同的類派生出一個類來
. 從1個或多個父類(parent class) / 基類(base class)繼承,即繼承父類的屬性和行爲,但也有自己的特有屬性和行爲.

#include <iostream> 
#include <string> 
using namespace std;
class Employee{
	string name;
public:
	Employee(string n);
	void print();
};
class Manager: public Employee{    //子類
	int level;
public:
	Manager(string n, int l = 1);
	//void print();
	};

Employee::Employee(string n) :name(n)//初始化成員列表  
{
	//name = n; 	
}
void Employee::print() {
	cout << name << endl;
}

Manager::Manager(string n, int l) :Employee(n), level(l) {
}
//通過調用基類的構造函數對基類成員初始化
//派生類的構造函數只能描述它自己的成員和其直接基類的初始式,不能去初始化基類的成員。 
Manager::Manager(string n, int l) : name(n), level(l) {  //錯的
}
int main() {
	Manager m("Zhang",2);
	Employee e("Li");
	m.print();
	e.print();
}
//子類和父類有同名函數
class Manager : public Employee
{
	int level;
public:
	Manager(string n, int l = 1);
	void print();
};
Manager::Manager(string n, int l) :Employee(n), level(l) {
}
void Manager::print() {
	cout << level << "\t";
	Employee::print();
}
int main() {
	Manager m("Zhang");
	Employee e("Li");
	m.print();     //打印的是manage的print
	e.print();
}

派生類的指針可以自動轉化爲基類指針

int main() {
	Employee *p;
	Manager m("Zhang", 1);
	p = &m;
	p->print();   //此時打印的是employee中成員不是manage的
}

16.虛函數Virtual Functions,多態

上面代碼可以將print聲明爲虛函數Virtual Functions :用virtual關鍵字,根據實際指向的類型輸出,多態性,否則指針輸出的是基類
靜態成員函數不能是虛函數。

1.基類中必須包含虛函數(在成員函數之前加上virutal 關鍵字),並且在派生類中對基類中的虛函數進行重寫
2.在派生類中重寫的函數在基類中必須是虛函數(派生類中可以加virtual 關鍵字,不加的時候仍然會保持虛函數特性,但是建議加上)
3.派生類中虛函數必須與基類中虛函數的原型保持一致(返回值,函數名,參數列表和返回值都要相同,如果不同則會構成同名隱藏)
例外:析構函數(函數名不同)
例外:協變:基類的虛函數返回基類的引用或指針,派生類的虛函數返回派生類的引用或指針
4.基類與派生類中函數的訪問權限可以不同,不過基類中虛函數必須是public權限
5.通過基類的指針或者引用來調用虛函數
6.靜態成員函數不能定義爲虛函數

多態:對於通過基類指針調用基類和派生類中都有的同名、同參數表的虛函數的語句,編譯時並不確定要執行的是基類還是派生類的虛函數;而當程序運行到該語句時,如果基類指針指向的是一個基類對象,則基類的虛函數被調用,如果基類指針指向的是一個派生類對象,則派生類的虛函數被調用。這種機制就叫作“多態。

17.純虛函數和抽象類

主要作用:相當於上述的基類,抽象類專門用來存放多態的接口,它是所有要實現多態的類的基類。
一 般的抽象類只是爲了構成多態,並且其中的純虛函數只是爲了在其它的類種進行重寫。
如果一個類繼承於一個虛函數,那麼這個函數種必須要重寫抽象類中的純虛函數。
寫法:只有public的成員函數,並且只是聲明,而且聲後面加上 = 0,此函數叫做純虛函數。
構成:以純虛函數作爲成員函數的類叫做抽象類,抽象類不能直接實例化,但是抽象類的指針是可以定義的。

18.內聯函數:inline

內聯是以代碼膨脹(複製)爲代價,僅僅省去了函數調用的開銷,從而提高函數的執行效率。如果執行函數體內代碼的時間,相比於函數調用的開銷較大,那麼效率的收穫會很少。另一方面,每一處內聯函數的調用都要複製代碼,將使程序的總代碼量增大,消耗更多的內存空間。

void Foo(int x, int y);   
inline void Foo(int x, int y) // inline與函數定義體放在一起  
{   
…  
}   

對於任何內聯函數,編譯器在符號表裏放入函數的聲明(包括名字、參數類型、返回值類型)。如果編譯器沒有發現內聯函數存在錯誤,那麼該函數的代碼也被放入符號表裏。在調用一個內聯函數時,編譯器首先檢查調用是否正確(**進行類型安全檢查,或者進行自動類型轉換,**當然對所有的函數都一樣)。如果正確,內聯函數的代碼就會直接替換函數調用,於是省去了函數調用的開銷。這個過程與預處理有顯著的不同,因爲預處理器不能進行類型安全檢查,或者進行自動類型轉換。假如內聯函數是成員函數,對象的地址(this)會被放在合適的地方,這也是預處理器辦不到的。

  C++ 語言的函數內聯機制既具備宏代碼的效率,又增加了安全性,而且可以自由操作類的數據成員。所以在C++ 程序中,應該用內聯函數取代所有宏代碼,“斷言assert”恐怕是唯一的例外。assert 是僅在Debug版本起作用的宏,它用於檢查“不應該”發生的情況。爲了不在程序的Debug版本和Release版本引起差別,assert 不應該產生任何副作用。如果assert是函數,由於函數調用會引起內存、代碼的變動,那麼將導致Debug版本與Release版本存在差異。所以assert 不是函數,而是宏。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章