NDK學習之路之 c++ 知識概括

1.c++ 引用

其實就是四驅模型的拷貝,引用實際上是地址賦值,能夠當作同一塊內存的另一個變量 以兩個變量值交換爲例html

#include<stdio.h>
void swap(int &number1, int &number2) {
	// &number 此處至關於同一塊內存 取了另外一個別名
	int temp = number1;
	number1 = number2;
	number2 = temp;
}
void main() {
	int number1 = 10;
	int number2 = 20;
	swap(number1, number2);
	printf("%d %d\n", number1, number2); // 結果 20 10
	getchar();
}
複製代碼

2.常量指針和指針常量

常量變量:被常量修飾的變量,不能再次被賦值 (Java)java

常量指針(const int *p):const 在 * 以前,指針的地址是能夠被再次賦值的(能夠修改的),指針地址上面的值(變量)是不能被修改的,常量指針的常量是不能被改變的。指向常量的指針,表示不能夠修改p的內容,可是能夠修改 p 的地址.ios

指針常量(int *const p):const 在 * 以後,指針的地址是不能夠被再次賦值的(不能夠修改的),指針地址上面的值(變量)能被修改的,指針常量的指針地址是不能被改變的。指針的常量,表示不能夠修改p的地址,可是能夠修改 p 的內容.c++

// 常量,不能去修改
	const int number = 100;
	// number = 200;   // error

	int number1 = 100;
	int number2 = 200;

	// 常量指針
	// int const * n_p = &number2;
	// n_p = &number1;
	// printf("n_p = %p",n_p); // 地址是能夠從新被賦值的 
	// *n_p = 300; // 值是不能改的 error

	// 指針常量
	int * const n_p = &number2;
	// n_p = &number1; // 地址是不能被從新賦值
	*n_p = 300;
	printf("number2 = %d", number2);// 值能夠被修改,結果:300
複製代碼

3.malloc,free,new,delete 區別

malloc/free 一對,new/delete 是一對程序員

malloc/free 不會去調用構造函數和析構函數算法

new/delete 會去調用構造函數和析構函數 若是用了new,必定要記得 delete 釋放內存安全

4.構造函數、析構函數和拷貝構造函數

直接上代碼說明bash

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>

using namespace std;

class Student
{
public:
	// 構造函數
	Student(){// 空參數構造函數
		cout << "空參數構造函數"<< endl;
	}

	// Student(char* name):age(0){// 一個參數構造函數, 至關於 this->age = 0
	//	cout << "一個參數構造函數" << endl;
	//	this->name = name;
	// }

	Student(char* name) :Student(name,0){
     // 調用兩個參數的構造函數
     // 注意:先會調用兩個參數的構造函數,而後纔會執行當前構造函數
     cout << "一個參數構造函數" << endl;
	}

	Student(char* name, int age){// 兩個參數構造函數
		cout << "兩個參數構造函數" << endl;
		this->name = (char*)malloc(sizeof(char)*100);
		strcpy(this->name,name);
		this->age = age;
	}

	// 2. 析構函數,若是有在對象內部開闢堆內存,能夠在析構函數中釋放內存
	~Student(){
		cout << "析構函數" << endl;
		// 對象被回收的時候會被調用
		// 釋放內存
		free(this->name);
		this->name = NULL;
	}

	/*
	4.拷貝構造函數,在 java 中若是 Student stu2 = stu1,那麼咱們通常會認爲 stu2 對象和 stu1 對象是同一個對象,
	可是在 c++ 中這種認爲就是錯誤的,咱們能夠分別打印 stu1 和 stu2 的地址發現並不相等,
	因此 stu1 和 stu2 並非同一個對象,而是會調用拷貝構造函數。*/
	// 對象會有一個默認的拷貝構造函數,用來對象之間的賦值
	Student(const Student& stu){// 常量的引用
		cout << "拷貝構造函數" << endl;
		// this->name = stu.name;// 淺拷貝
		// 若是動態開闢內存,必定要採用深拷貝
		this->name = (char*)malloc(sizeof(char)* 100);
		strcpy(this->name, stu.name);
		this->age = stu.age;
	}
	
	Student getStudent(char* name){
    Student stu(name);// 棧 ,方法執行完,這個對象會被回收,可是發現調用了拷貝構造函數
    cout << &stu << endl;
    return stu;// 會返回一個新的 Student 對象,而棧內開闢的 stu 是會被回收
}


void printStudent(Student stu){// stu 是該方法棧中一個新的對象,拷貝構造函數賦值,方法執行完會調用析構函數
    cout << stu.getName() << " , " << stu.getAge() << endl;
}

void main(){
    // 1. = 會調用拷貝構造函數
    // Student stu1("Darren", 24);
    // Student stu2 = stu1; // = 是賦值,把裏面全部定義的屬性賦值,c/c++ 編譯器幫咱們作的,其實會調用對象的拷貝構造

    // Student stu2;// 聲明變量,開闢變量內存
    // stu2 = stu1; // 這個不會去調用拷貝構造函數,可是會賦值 c 的相似

    // 2. 第二種場景 做爲參數返回的時候會調用拷貝構造函數
    // Student stu = getStudent("Jack");
    // cout << &stu << endl;

    // 3. 第三種場景 做爲參數傳遞的時候會調用拷貝構造函數
    // Student stu("Darren", 24);
    // printStudent(stu);

    // 知識的補充
    Student stu = getStudent("Jack");

    // cout << stu.getName() << " , " << stu.getAge() << endl;

    printStudent(stu);

    getchar();
}

private:
	int age;
	char* name;
 
public:
	int getAge(){
		return this->age;
	}

	char* getName(){
		return this->name;
	}

	void setAge(int age){
		this->age = age;
	}

	void setName(char* name){
		this->name = name;
	}
};
複製代碼

5.內存四驅模型

在 c/c++ 中咱們將運行時數據,分爲四個區域分別是:棧區,堆區,數據區,代碼區。咱們詳細來介紹下:數據結構

  • 棧區:由編譯器自動分配釋放 ,存放函數的參數值,局部變量的值等。
  • 堆區:通常由程序員分配釋放, 若程序員不釋放,程序結束時可能由 OS 回收。
  • 數據區:存放全局變量、靜態變量和常量字符串等等。 程序結束後由系統釋放。
  • 存放函數體的二進制代碼。

6.可變參數

#include <iostream>
#include <stdarg.h>
using namespace std;

int sum(int count, ...) {  // 格式:count表明參數個數, ...表明n個參數
	va_list ap;  // 聲明一個va_list變量
	va_start(ap, count);  // 第二個參數表示形參的個數

	int sum = 0;
	for (int i = 0; i < count; i++) {
		sum += va_arg(ap, int);   // 第二個參數表示形參類型
	}
	va_end(ap);  // 用於清理
	return sum;

}
void main(){
	int sumValue = sum(3,8,10,9);
	cout << sumValue << endl;   // 輸出結果:27
	getchar();
}
複製代碼

步驟:app

  1. 定義頭文件 stdarg.h

  2. 定義參數:參數格式 (int count, ...)

  3. 函數定義中建立一個 va_list 變量

  4. 初始化: va_start(ap, count);

  5. 循環將各個參數進行相加

  6. 清理:調用清理 va_end(va_list); 清理內容,最後調用

7.static 關鍵字

  1. 靜態屬性在 c++ 中必需要初始化
class Test {
    public static int tag;
}
int Test::tag = 1;   // 只能這樣初始化
複製代碼

8.C++程序在內存中的分佈

  1. 棧(Stack):局部變量,函數參數等存儲在該區,由編譯器自動分配和釋放.棧屬於計算機系統的數據結構,進棧出棧有相應的計算機指令支持,並且分配專門的寄存器存儲棧的地址,效率很高,內存空間是連續的,但棧的內存空間有限。
  2. 堆(Heap):須要程序員手動分配和釋放(malloc,free),屬於動態分配方式。內存空間幾乎沒有限制,內存空間不連續,所以會產生內存碎片。操做系統有一個記錄空間內存的鏈表,當收到內存申請時遍歷鏈表,找到第一個空間大於申請空間的堆節點,將該節點分配給程序,並將該節點從鏈表中刪除。通常,系統會在該內存空間的首地址處記錄本次分配的內存大小,用於 free 釋放該內存空間。
  3. 全局/靜態存儲區:全局變量,靜態變量分配到該區,到程序結束時自動釋放,包括 DATA 段(全局初始化段)與 BBS 段(全局未初始化段)。其中,初始化的全局變量和靜態變量存放在 DATA 段,未初始化的全局變量和靜態變量存放在 BBS 段。BBS 段特色:在程序執行前 BBS 段自動清零,因此未初始化的全局變量和靜態變量在程序執行前已經成爲 0.
  4. 文字常量區:存放常量,並且不容許修改。程序結束後由系統釋放。
  5. 程序代碼區:存放程序的二進制代碼

9.類的大小

類中只計算除了靜態屬性的大小之和,方法什麼的都不算,但定義字節數大的要排在字節數小的,例如 double 屬性要在 int 以前,若定義不按此規則,則類的大小可能會變大,這樣作有利於提升性能

10.友元函數、友元類

友元函數

class A{
    // 友元函數聲明
    friend void modify_i(A *p, int a);
private:
    int i;
public:
    A(int i){
        this->i = i;
    }
    void myprint(){
        cout << i << endl;
    }   
};

// 友元函數的實現
void modify_i(A *p, int a){
    p->i = a;
}

void main(){
    A* a = new A(10);
    a->myprint();
    modify_i(a,20);
    a->myprint();
}
複製代碼

友元類

class A{        
    // 友元類聲明,表示 B 這個友元類能夠訪問 A 類的任何成員
    friend class B;
private:
    int i;
public:
    A(int i){
        this->i = i;
    }
    void myprint(){
        cout << i << endl;
    }   
};

class B{
private:
    A a;
public:
    void accessAny(){
        a.i = 30;       
    }
};
複製代碼

11.虛函數

至關於 Java 的抽象,解決繼承二義性問題

virtual 關鍵字,確保繼承過來的相同屬性或者函數,只存在一份拷貝

  • 當一個類具備一個純虛函數,這個類就是抽象類
  • 抽象類不能實例化對象
  • 子類繼承抽象類,必需要實現純虛函數,若是沒有,子類也是抽象類
class A{
public:
	char* name;
};

class B : virtual public A{ // 確保繼承過來的相同屬性或者函數,只存在一份拷貝
	
};

class C :virtual public A{

};

class D : public B ,public C
{

};
複製代碼

12.模板函數

至關於 Java 的泛型

template <typename T>// 模板函數的定義
T add(T number1, T number2){
	return number1 + number2;
}

void main(){
	
	int sum1 = add(1,2);

	cout << sum1 << endl;

	int sum2 = add(1.0, 2.0);

	cout << sum2 << endl;

	int sum3 = add(1.0f, 2.0f);

	cout << sum3 << endl;

	getchar();
}
複製代碼

當同名普通函數和模板函數同時存在的時候,優先會調用普通函數

13. 類型轉換

  1. static_cast 類型轉換,意圖明顯,增長可閱讀性,靜態轉換 用於基本數據類型之間的轉換,如把int轉換成char
  2. const_cast 常量指針轉換 用於修改常量的值
  3. reinterpret_cat 強制類型轉換 ,用於轉換任意類型,函數指針轉型,不具有移植性
  4. dynamic_cast 動態轉換 ,更安全,轉換成功返回類型,失敗返回空 , 必需要包含多態類型,和 static_cast 很相似,可是更安全,經常使用於子類類型轉爲父類類型

例:父類(Person)轉子類(Student)

// 1.static_cast,通常用於轉換有繼承關係的類型 
Student *student = static_cast<Student*>(person);
// 2.reinterpret_cast,除了字符類型,各類類型的轉換,經常使用
Student *student = reinterpret_cast<Student*>(person);
複製代碼

14.異常處理

  1. 在 c++ 層若是是本身寫的代碼或者調用別人的方法,記得要 try 住, 若是不 try 在 java 層 try 是沒有意義的
  2. 若是異常須要往外拋給 java 層,必定要按照java層拋異常的方式
  3. 若是是本身寫的 NDK 層的代碼,最好拋本身寫的異常,聲明異常
  4. 若是是作 c++/c , 或者是幫 c/c++ 寫代碼,最好拋系統定義好的異常或者繼承系統的異常
  5. 系統異常的體系 exception 基類 www.cnblogs.com/QG-whz/p/51…

15.string 和 char* 互轉

// string 轉 char*
string str = "666";
const char* str2char = str.c_str();

// char* 轉 string
char* char2str = "666"
string strValue(char2str);
複製代碼

16. 字符串的遍歷

string str("1234567");

	// 1. 字符串的遍歷
	for (int i = 0; i < str.length(); i++)
	{
		cout << str[i] << endl;
		// or
		cout << str.at(i) << endl;
	}

	// 2. 迭代器遍歷
	for (string::iterator it = str.begin(); it < str.end(); it++)
	{
		cout << *it << endl;
	}
複製代碼

17.添加、刪除、替換、查找、大小寫轉換

  • 添加
string str1 = "123";
	string str2 = "456";

	str1 = str1 + str2;
	str1.append(str2);
複製代碼
  • 刪除
string str1 = "123456789";
    // 第一個參數:從哪裏開始 ; 第二個參數:刪除幾個(默認值,字符串的結尾)
	str1.erase(0, 3);

	// 迭代器刪除
	for (string::iterator it = str1.begin(); it<str1.begin() + 3; it++)
	{
	    // 刪除一個字後都會從頭開始計算
		str1.erase(it);
	}
複製代碼
  • 替換
string str1 = "123456789";
	// 第一個參數:從哪裏開始
	// 第二個參數:替換幾個
	// 第三個參數:替換成誰
	str1.replace(0, 6, "0000");
	// 結果:前六個替換成 0000
複製代碼
  • 查找
string str1 = "123abc123abc123";
	// (查找誰,從哪裏開始),查不到返回 -1
	// int position = str1.find("123",0);
	// 從後面往前面查
	int position = str1.rfind("123");
複製代碼
#define D_SCL_SECURE_NO_WARNINGS
#include <iostream>
#include <algorithm>// STL 算法包
#include <cctype> // 用到一個函數指針,回調函數

    string str1 = "AAA abc BBB abc 123";
	// 轉換成大寫
	transform(str1.be gin(), str1.end(), str1.begin(), toupper);
    // 轉換成小寫
	transform(str1.begin(), str1.end(), str1.begin(), tolower);
複製代碼

18.引用知識系列

1. 引用增強
// 其實引用本質就是 指針
void change(int& number1){// 至關於 c 的 change(int* number1)
	number1 = 12;// 至關於 c 的 *number1 = 12; 
}
複製代碼
2. 引用的使用
void main(){
    int a = 10;
    // &是C++中的引用,引用:變量的另一個別名,共用同個地址
    int &b = a; 
    cout << b << endl;
}
複製代碼
3. 引用與指針的區別
  • 不存在空引用,引用必須鏈接到一塊合法的內存。
  • 一旦引用被初始化爲一個對象,就不能被指向到另外一個對象。指針能夠在任什麼時候候指向到另外一個對象。
  • 引用必須在建立時被初始化。指針能夠在任什麼時候間被初始化。
4. 引用與指針寫法上的差別
struct Teacher{
    char* name;
    int age;
};
// 帶有結構體指針的寫法
void myprint(Teacher *t){
    cout << t->name << "," << t->age << endl;   
    //(*t).name 
}
// 帶有結構體引用的寫法
void myprint2(Teacher &t){
    cout << t.name << "," << t.age << endl;
    t.age = 21;
}
// 指針值交換
void swap_1(int *a, int *b){
    int c = 0;
    c = *a;
    *a = *b;
    *b = c;
}
// 引用值交換
void swap_2(int &a, int &b){
    int c = 0;
    c = a;
    a = b;
    b = c;
}
void main(){
    Teacher t;
    t.name = "Vegen";
    t.age = 24;
    // 指針的寫法
    myprint(&t);
    // 引用的寫法
    myprint2(t);

    int x = 10;
    int y = 20;
    // 指針的寫法
    swap_1(&x, &y);
    // 引用的寫法
    swap_2(x,y);
}
複製代碼
5. 引用的做用
  • 把引用做爲參數:C++ 支持把引用做爲參數傳給函數,這比傳通常的參數更安全
  • 把引用做爲返回值:能夠從 C++ 函數中返回引用,就像返回其餘數據類型同樣
6. 指針引用,代替二級指針
struct Teacher{
    char* name;
    int age;
};
// 引用的寫法
void getTeacher(Teacher* &p){
    p = (Teacher*)malloc(sizeof(Teacher));
    p->age = 24;
}
// 二級指針的寫法,本來應該這樣寫,可是已經被上面引用的寫法代替了
void getTeacher(Teacher **p){
    Teacher *tmp = (Teacher*)malloc(sizeof(Teacher));
    tmp->age = 24;
    *p = tmp;
}

void main(){
    Teacher *t = NULL;
    //傳遞引用的指針t,至關於二級指針
    getTeacher(&t);
}
複製代碼
7. 常引用,相似於 java 中 final
// 常引用在方法中的引用
void myprint(const int &a){
    cout << a << endl;  
}

void main(){    
    // 引用必需要有值,不能爲空,下面寫法是錯誤的
    //const int a;
    //int &a = NULL;

    // 常引用屬性使用一
    int a = 10, b = 9;
    const int &c = a;
    // 常引用屬性使用二
    const int &d = 70;
}
複製代碼
8. 引用與指針的大小
struct Teacher{
    char name[20];
    int age;
};

void main(){
    Teacher t;
    // 引用
    Teacher &t1 = t;
    // 指針
    Teacher *p = &t;

    // 結果是 24,引用至關於變量的別名
    cout << sizeof(t1) << endl;
    // 結果是 4,指針只是存放的地址
    cout << sizeof(p) << endl;
    system("pause");
}
複製代碼

19. 運算符重載

1. 運算符重載的寫法一
class Point{
public:
    int x;
    int y;
public:
    Point(int x = 0, int y = 0){
        this->x = x;
        this->y = y;
    }
    void myprint(){
        cout << x << "," << y << endl;
    }
};
// 重載 + 號
Point operator+(Point &p1, Point &p2){
    Point tmp(p1.x + p2.x, p1.y + p2.y);
    return tmp;
}
// 重載 - 號
Point operator-(Point &p1, Point &p2){
    Point tmp(p1.x - p2.x, p1.y - p2.y);
    return tmp;
}

void main(){
    Point p1(10,20);
    Point p2(20,10);
    Point p3 = p1 + p2;
    // 輸出結果 30,30
    p3.myprint();
}
複製代碼
2. 運算符重載的寫法二
class Point{
public:
    int x;
    int y;
public:
    Point(int x = 0, int y = 0){
        this->x = x;
        this->y = y;
    }
    // 成員函數,運算符重載
    Point operator+(Point &p2){
        Point tmp(this->x + p2.x, this->y + p2.y);
        return tmp;
    }
    void myprint(){
        cout << x << "," << y << endl;
    }
};

void main(){
    Point p1(10, 20);
    Point p2(20, 10);
    // 運算符的重載,本質仍是函數調用
    //p1.operator+(p2)
    Point p3 = p1 + p2;
    p3.myprint();
}
複製代碼

持續更新中...

相關文章
相關標籤/搜索