《 android NDK 》 C++語言入門總結

-前言

上次總結了C語言的知識點,這次總結一下C++的學習心得。C++擴充和完善了C語言,是一門面向對象的編程語言。

-hello world

我們先看看hello world的例子:

#include <iostream>
using namespace std;
int main() {
	cout << "hello world!" << endl;
	return 0;
}

1.#include <iostream> c++提供給開發者的一些頭文件;
2.using namespace std; 命名空間

-命名空間

C++中命名空間,作爲附加信息來區分不同庫中相同名稱的函數、類、變量等。例如,我們現在有兩個頭文件中都有print()函數,那我們就需要頭文件來區分。

first.h:在頭文件first.h中寫一個print()函數,

#pragma once
#include<iostream>

void print() {
	std::cout << "頭文件1" << std::endl;
}

再創建一個頭文件two.h,也實現print()函數(這裏和上面first.h代碼一樣),我們在.cpp中同時引入這兩個頭文件,如下:

#include "first.h"
#include "two.h"
#include <iostream>

using namespace std;

int main() {
	
	print();
	return 0;
}

在main函數中調用print()函數。這時候就會有問題,print()到底是調用哪個頭文件下的呢?爲了區分這print(),我們需要使用命名空間。在頭文件first.h中定義命名空間:

namespace ftd {
	void print() {
		std::cout << "頭文件1" << std::endl;
	}
}

在main中使用命名空間

int main() {
	ftd::print();
	return 0;
}

這裏就可以直接調用first.h中的print()函數了。當然也可以像下邊這樣使用:

using namespace ftd;
print();

-引用變量

引用變量可以理解爲是變量的一個別名。

    int a = 1;
	int& b = a;

	cout << a << endl;
	cout << b << endl;

在這裏 a,b打印出來的值是一樣的。這裏需要注意的是引用變量聲明的時候必須初始化。

引用變量作爲參數

void swap(int a,int b) {
	a ^= b;
	b ^= a;
	a ^= b;
}

void swap1(int* a, int* b) {
	*a ^= *b;
	*b ^= *a;
	*a ^= *b;
}

void swap2(int& a, int& b) {
	a ^= b;
	b ^= a;
	a ^= b;
}

int main() {
	
	int a = 3;
	int b = 4;

	swap(a, b);
	printf("a , b 的值:%d,%d \n",a,b);
	swap1(&a,&b);
	printf("a , b 的值:%d,%d \n", a, b);
	swap2(a, b);
	printf("a , b 的值:%d,%d \n", a, b);

	return 0;
}

上面的三個函數 swap,swap1,swap2中第二個和第三個函數分別用指針和引用變量作爲形參。可以發現第一個函數因爲是值傳遞,main中的a,b兩個參數的值並沒有發生變化;第二個使用的指針,因爲指針指向的值發生改變,所以main函數的a,b值換了;第三個傳的是變量本身,因此也可以改變傳入變量本身的值。

引用變量作爲返回值

int getA1() {
	static int a = 15;
	return a;
}

int& getA2() {
	static int a = 10;
	return a;
}

int* getA3() {
	static int b = 3;
	return &b;
}

int main() {
	
	cout << getA1() << endl;
//	getA1() = 1;
	cout << getA2() << endl;
	getA2() = 2;
	cout << getA2() << endl;
	cout << *getA3() << endl;
	*getA3() = 2;
	cout << *getA3() << endl;
	return 0;
}

首先三個函數中返回的是靜態變量,因爲局部變量在函數執行完會被釋放。其次,我們可以發現函數getA2()和getA3()可以作爲左值被賦值。因爲getA2()返回的是變量,getA3()返回的是指針。getA1()返回的是一個int類型的值,所以getA1()不能作爲左值。

-函數

內聯函數

inline void printA() {
	cout << "printA" << endl;
}

int main() {
	printA();
	return 0;
}

上面 printA() 即內聯函數,在函數的前面聲明inline。內聯函數C++編譯器會將函數裏的代碼嵌入到調用該函數的函數中,減少了函數入棧出棧的開銷。但是聲明瞭inline,編譯器不一定就會讓該函數內聯。我們需要注意以下幾點:

// 1.內聯函數聲明時候必須實現,不能分開。分開的話,編譯器不會內聯;
// 2.內聯函數必須在調用函數的前面;
// 3.內聯函數中不能存在任何形式的循環,不然編譯器不會內聯;
// 4.內聯函數中不能存在過多的條件語句,不然編譯器不會內聯;
// 5.內聯函數不能過於龐大;
// 6.內聯函數不能進行取地址操作;

必須遵循以上幾點,我們聲明的內聯函數編譯器纔可能允許內聯。還有一點有的沒有聲明內聯的函數編譯器也可能將其內聯。

默認參數

//默認參數
void print(int i=4) {
	cout << i << endl;
}

int main() {
	print();
	print(2);
	return 0;
}

在C++中形參是可以設置默認值的,像上面一樣,print()中 i 打印出來是 4 ;print(2)中 i 打印的是2。也就是如果函數傳參了,用傳的值,否則用默認值。

//默認參數
void print(int a,int b=4) {
	cout <<"a:"<<a<<"b:"<<b<< endl;
}
void print1(int a, int b = 4,int c) { //這裏是錯誤的
	cout << "a:" << a << "b:" << b << endl;
}
int main() {
	print(1);
	print(1,2);
	return 0;
}

print1()函數是報錯的,當參數中有默認值出現嗎,那麼後面的參數也必須有默認值。

-類

類和類函數的聲明一般在頭文件中,

class First
{
private:
	int a;
public:
	void setA(int a);
	int getA();
};

函數的實現在cpp中:

void First::setA(int a) {
	this->a= a;
}

int First::getA() {
	return a;
}

類的使用:

int main() {
	First first;
	first.setA(2);
	cout << first.getA() << endl;
	return 0;
}

和java中是不是有點相似。

構造函數

class Test
{
public:	
	Test() {
	}
	Test(int a) {
	}
	Test(int a,int b) {
	}
	Test(const Test& test) {
	}
private:
};

以上便是C++中的構造函數,基本和java相似。最後一個構造函數是拷貝構造函數,也是構造函數的一種。

int main() {	
	//1.調用無參構造函數
	Test test;
	//2.
	Test test0();

	//3.調用有參構造函數
	Test test1(1,2);

	//4.
	Test test2 = 1;
	//5.這種調用,調用一個參數的構造函數
	Test test3 = (1, 2);

	//6.手動調用構造函數
	Test test4 = Test(1);

	//7.調用的拷貝構造函數
	Test test5 = test1;
	//8.
	Test test6(test1);
	return 0;
}

以上是幾種類聲明的方法。這裏注意幾點:

  1. 當類中沒有定義構造函數,系統會提供一個默認的無參構造函數;當類中有構造函數,系統將不提供無參構造函數。(和java中不同);
  2. 註釋 5 :無論傳多少個參數,都是使用的最後一位的參數去調用的一個參數的構造函數;
  3. 註釋 7 8 : 調用的是拷貝構造函數;
  4. 形參也會調用拷貝構造函數 (如果類中沒有拷貝函數,則調用系統中的);
  5. 構造函數中不能再調用別的構造函數;

析構函數

~Test(){
	}

這就是析構函數,構造函數前面有一個~標識符。在該對象釋放的時候調用,用於釋放類內的資源。

初始化列表

class A
{
public:
	A(int a) {
		this->a = a;
		cout << a << endl;
	}
	~A() {
	}
private:
	int a;
};

class B {
public :
	B(int c):a1(1),a2(c){
	}
	
private:
	int b;
	A a1;
	A a2;
};

int main() {
	B b(3);
	return 0;
}

像上面代碼,B類中含有A類對象並且A類沒有無參構造函數的時候,在構造B類的時候需要初始化A類。如上 className() : A類變量名(參數值),A類變量名(參數值);並且這些A類變量的初始化順序是和他們聲明時的順序一致。
初始化列表的使用場景:

  1. 成員變量是一個類類型,而且類中只要有參數的構造函數;
  2. const 變量
  3. 初始化父類的構造函數( 父類出現在初始化列表時,優先父類初始化再按初始化順序初始化別的類成員)

對象的動態創建和釋放

在棧上創建/釋放對象和堆上創建/釋放對象的區別:

  1. 在棧上創建的對象,創建後大小無法改變 (堆上可以動態調整);
  2. 棧上創建的對象,系統自動創建和銷燬 (堆上申請的空間必須手動申請和釋放);

堆上申請空間/釋放空間方法:c語言:malloc/calloc free; c++: new/delete new[]/delete[];

//new 分配內存
	int* p2 = new int(0);
	cout << *p2 << endl;
	*p2 = 10;
	cout << *p2 << endl;
	delete p2;
	p2 = nullptr;

	//new int[]
	int* p3 = new int[10];
	p3[2] = 2;
	cout << p3[2] << endl;
	delete[] p3;
	p3 = nullptr;

這裏注意:
1. 我們可以直接 new int(10) 初始int值爲10;
2. new 申請的內存 delete 釋放;new[]申請的內存 delete[]釋放;

class A
{
public:
	A(int a) {
	}
	~A();
}
int main() {
	A* a = new A(10);
	return 0;
}

new 爲複雜對象申請內存也可以直接設置默認值。

-友元函數/友元類

友元函數

友元函數:定義在類的外部且函數內部的類對象可以直接操作該對象的private/protected屬性和函數。
友元函數的聲明也很簡單,在函數的前面加上 friend 即可。如下:

class A{
public:
	A(int c) {
	}
	
private:
	friend void test(A a);
	void print() {
		cout << "友元調用" << endl;
	}
	int c;
};

void test(A testA) {
	testA.print();
	cout << testA.c << endl;
}

int main() {
	return 0;
}

上面的代碼中:函數test()中的testA參數雖然是A類型的,但是是不可以使用A類中私有的 c元素和print()函數的。
但是,我們在A類中通過這行代碼 friend void test(A a) 將這個函數聲明爲友元函數就可以了。
這裏需要注意的是:友元函數的聲明在public/private/protected中都可以。

友元類

友元類:假設在A類中聲明瞭友元類B,則在B類中可以使用A類中任何類型的元素和函數。
友元類的聲明:friend class 類名;

class A
{
public:
	A() {}
private:
	friend class B;
	void print() {
		cout << number << endl;
	}
	int number;
};

class B
{
public:
	B() {}
private:
	void print() {
		testA.number;
	}
	A testA;
};

我們可以發現,在A中聲明的友元類B中的testA對象可以直接使用number元素。

-繼承

當創建一個類時,您不需要重新編寫新的數據成員和成員函數,只需指定新建的類繼承了一個已有的類的成員即可。這個已有的類稱爲基類,新建的類稱爲派生類
在C++中,一個派生類可以派生很多基類(就是可以有很多父類)。派生列表格式如下:

class derived-class: access-specifier base-class

derived-class:派生類的名稱;base-class 基類的名稱;access-specifier 繼承類型。

class parent
{
public:
	parent() {}
	int number1;
private:
	int number;
};

class child : public parent
{
public:
	child() {
	}
private:	
};

這裏注意:

  1. 派生類中可以訪問除了private修飾的所有基類的成員。
  2. 派生類繼承所有基類函數,除了:構造函數,析構函數,拷貝構造函數,重載運算符 和 友元函數。
  3. 基類的構造函數,派生類用初始化列表調用;
  4. 類的繼承類型按以下規則:
    1. public 繼承基類的public 和 protected類型在派生類中仍是public 和 protected;
    2. protected 繼承基類 public 和 protected類型變成派生類中的 protected類型;
    3. private 繼承基類 public 和 protected類型變成派生類中的 private 類型;
public protected private
同一個類 yes yes yes
派生類 yes yes no
外部的類 (public繼承) yes no no
外部的類 (protected繼承) no no no
外部的類 ( private繼承) no no no

-抽象類

class Shap
{
public:
	virtual void area()=0;
};

class Circle:public Shap{
public:
	Circle(int a) :a(a) {
	}
	void area() {
		cout << 3.14 * a * a << endl;
	}
private:
	int a;
};


int main() {
	Shap* shap = new Circle(2);
	shap->area();

	return 0;
}

C++ 接口是使用抽象類來實現的,如果類中至少有一個函數被聲明爲純虛函數,則這個類就是抽象類。純虛函數是通過在聲明中使用 “= 0” 來指定的,上文中的Shap中的area()函數就是純虛函數,並且Shap類不能被實例化。

-模板

template <typename T>
T const Max(T a,T b) {
	return a < b ? b : a;
}

int main() {
	int a = 2;
	int b = 3;
	cout << Max(3,5) << endl;
	cout << Max(1.2,2.3) << endl;
	return 0;
}

上面代碼中使用的是c++中的模板,和java中的泛型有點類似。Max方法可以比較 int double的值的大小。一般模板函數的寫法如下:

template <typename type>
ret-type func-name( list)
{
   // 函數的主體
}

type: type 是函數所使用的數據類型的佔位符名稱,由我們自定義;
ret-type:函數返回類型;
func-name:函數名稱;

類模板的聲明如下:

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