天天看點

文法基礎——C++文法基礎

前言

最近發現要學習C++來開發NDK,不得不把基礎的東西記錄下來,否則學的太多會混淆,廢話不多說,開始記錄我的C++學習之旅吧

HelloWord

  1. 導庫
  2. 命名空間
  3. 輸出函數
#include <iostream>
//必須帶有命名空間才能使用cout等
using namespace std;
int main()
{
    cout << "Hello, world!" << endl;
    return ;
}
           

命名空間

1、命名空間屬性通路和結構體通路

namespace NSP_A{
    int a = ;
    struct Student{
        char name[];
        int age;
    };
}

void main(){
    //使用命名空間
    cout << NSP_A::a << endl;

    //使用命名空間中的結構體
    using NSP_A::Student;
    Student t;
}
           

2、命名空間的嵌套

namespace NSP_B{
    //命名空間嵌套
    namespace NSP_C{
        int c = ;
    }
}

void main(){
    cout << NSP_B::NSP_C::c << endl;
}
           

1、類、屬性、方法的聲明與使用

#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <iostream>
#include <stdarg.h>
using namespace std;
#define PI 3.14
class MyCircle{
//屬性(共用權限通路修飾符)
private:
    double r;
    double s;
public:
    void setR(double r){
        this->r = r;
    }
    //擷取面積
    double getS(){
        return PI * r * r;
    }
};

void main(){
    MyCircle c;
    c.setR();

    cout << "圓的面積:" << c.getS() << endl;
    system("pause");
}
           

2、類的大小

類的大小隻計算普通屬性的大小,其他都不包括在内

class A{
public:
    int i;
    int j;
    int k;
    static int m;
};
class B{
public:
    int i;
    int j;
    int k;
    void myprintf(){
        cout << "列印" << endl;
    }
};
void main(){
    //輸出的結果都是12
    cout << sizeof(A) << endl;
    cout << sizeof(B) << endl;
}
           

繼承

1、繼承基本使用

class Human{
public:
    void say(){
        cout << "說話" << endl;
    }
protected:
    int age;    
};
class Man : public Human{

};

void main(){
    //子類
    Man m1;
    m1.say();
    //将子類指派給父類的引用或指針
    Human* h_p = &m1;
    h_p->say();
    Human &h1 = m1;
    h1.say();
    //子類對象初始化父類類型的對象
    Human h2 = m1;
    h2.say();
    //子類對象調用父類的成員
    m1.Human::say();
    m1.Human::age = ;
}
           

2、向父類構造方法傳參

class Human{
public:
    Human(char* name, int age){
        this->name = name;
        this->age = age;
    }
protected:
    char* name;
    int age;
};
class Man : public Human{
public:
    //給父類構造函數傳參,同時給屬性h對象指派
    Man(char *brother, char *s_name, int s_age, char *h_name, int h_age) : Human(s_name, s_age), h(h_name,h_age){
        this->brother = brother;
    }
private:
    Human h;
};

void main(){
    Man m1("Hensen","HensenBoy",,"HensenGirl",);
}
           

3、多繼承的實作

class Person{

};

class Citizen{

};

class Student : public Person, public Citizen{

};
           

4、繼承間的通路修飾符

基類中        繼承方式        子類中
public     & public繼承 = > public
public     & protected繼承 = > protected
public     & private繼承 = > private

protected  & public繼承 = > protected
protected  & protected繼承 = > protected
protected  & private繼承 = > private

private    & public繼承 = > 子類無權通路
private    & protected繼承 = > 子類無權通路
private    & private繼承 = > 子類無權通路
           

5、繼承的二義性

virtual:表示虛繼承,不同路徑繼承來的同名成員隻有一份拷貝,解決不明确的問題

class A{
public:
    char* name;
};
//這裡面加了virtual關鍵字
class A1 : virtual public A{

};
//這裡面加了virtual關鍵字
class A2 : virtual public A{

};
class B : public A1, public A2{

};

void main(){
    B b;    
    //如果程式不加virtual關鍵字就會導緻二義性,系統無法辨識哪個類的name屬性,會報錯
    b.name = "Hensen";
    //這裡通過指定父類顯示調用是可以的
    b.A1::name = "Hensen";
    b.A2::name = "Hensen";
}
           

多态

1、多态的基本使用

//Plane.h檔案
#pragma once
//普通飛機
class Plane{
public:
    virtual void fly();
    virtual void land();
};
//Plane.cpp檔案
#include "Plane.h"
#include <iostream>
using namespace std;
void Plane::fly(){
    cout << "起飛" << endl;
}
void Plane::land(){
    cout << "着陸" << endl;
}
           
//Jet.h檔案
#pragma once
#include "Plane.h"
//直升飛機
class Jet : public Plane{
    virtual void fly();
    virtual void land();
};
//Jet.cpp檔案
#include "Jet.h"
#include <iostream>
using namespace std;
void Jet::fly(){
    cout << "直升飛機在原地起飛..." << endl;
}
void Jet::land(){
    cout << "直升飛機降落在女神的屋頂..." << endl;
}
           
#include "Plane.h"
#include "Jet.h"
#include "Copter.h"
//業務函數
void bizPlay(Plane& p){
    p.fly();
    p.land();
}
void main(){
    Plane p1;
    bizPlay(p1);
    //直升飛機
    Jet p2;
    bizPlay(p2);
}
           

引用

1、引用的使用

void main(){
    int a = ;
    //&是C++中的引用,引用:變量的另外一個别名,共用同個位址
    int &b = a; 
    cout << b << endl;
}
           

2、引用與指針的差別

  • 不存在空引用,引用必須連接配接到一塊合法的記憶體。
  • 一旦引用被初始化為一個對象,就不能被指向到另一個對象。指針可以在任何時候指向到另一個對象。
  • 引用必須在建立時被初始化。指針可以在任何時間被初始化。

3、引用與指針寫法上的差異

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 = ;
}
//指針值交換
void swap_1(int *a, int *b){
    int c = ;
    c = *a;
    *a = *b;
    *b = c;
}
//引用值交換
void swap_2(int &a, int &b){
    int c = ;
    c = a;
    a = b;
    b = c;
}
void main(){
    Teacher t;
    t.name = "Hensen";
    t.age = ;
    //指針的寫法
    myprint(&t);
    //引用的寫法
    myprint2(t);

    int x = ;
    int y = ;
    //指針的寫法
    swap_1(&x, &y);
    //引用的寫法
    swap_2(x,y);
}
           

4、引用的作用

  • 把引用作為參數:C++支援把引用作為參數傳給函數,這比傳一般的參數更安全
  • 把引用作為傳回值:可以從C++函數中傳回引用,就像傳回其他資料類型一樣

5、指針引用,代替二級指針

struct Teacher{
    char* name;
    int age;
};
//引用的寫法
void getTeacher(Teacher* &p){
    p = (Teacher*)malloc(sizeof(Teacher));
    p->age = ;
}
//二級指針的寫法,原本應該這樣寫,但是已經被引用的寫法代替了
void getTeacher(Teacher **p){
    Teacher *tmp = (Teacher*)malloc(sizeof(Teacher));
    tmp->age = ;
    *p = tmp;
}

void main(){
    Teacher *t = NULL;
    //傳遞引用的指針t,相當于二級指針
    getTeacher(&t);
}
           

6、常引用,類似于java中final

//常引用在方法中的引用
void myprint(const int &a){
    cout << a << endl;  
}

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

    //常引用屬性使用一
    int a = , b = ;
    const int &c = a;
    //常引用屬性使用二
    const int &d = ;
}
           

7、引用與指針的大小

struct Teacher{
    char name[];
    int age;
};

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

    //結果是24,引用相當于變量的别名
    cout << sizeof(t1) << endl;
    //結果是4,指針隻是存放的位址
    cout << sizeof(p) << endl;
    system("pause");
}
           

異常

1、C++異常處理,會根據抛出的異常資料類型,進入到相應的catch塊中

void main(){
    try{
        int age = ;
        if (age > ){
            throw ;
        }
    }
    catch (int a){
        cout << "int異常" << endl;
    }
    catch (char* b){
        cout << b << endl;
    }
    catch (...){
        cout << "未知異常" << endl;
    }
}
           

2、向下抛出異常

void mydiv(int a, int b){
    if (b == ){
        throw "除數為零";
    }
}
void func(){
    try{
        mydiv(, );
    }
    catch (char* a){
        throw a;
    }
}
void main(){
    try{
        func();
    }
    catch (char* a){
        cout << a << endl;
    }
}
           

3、抛出對象

class MyException{

};
void mydiv(int a, int b){
    if (b == ){
        throw MyException();
        //不要抛出異常指針
        //throw new MyException;        
    }
}
void main(){
    try{
        mydiv(,);
    }
    catch (MyException& e2){
        cout << "MyException引用" << endl;
    }
    catch (MyException* e1){
        cout << "MyException指針" << endl;        
        //指針的話需要手動釋放記憶體
        delete e1;
    }
}
           

4、方法抛出異常

void mydiv(int a, int b) throw (char*, int) {
    if (b == ){
        throw "除數為零";   
    }
}
           

5、标準異常

//需要導入标準異常的依賴庫
#include<stdexcept>

class NullPointerException : public exception{
public:
    NullPointerException(char* msg) : exception(msg){

    }
};
void mydiv(int a, int b){
    if (b > ){
        throw out_of_range("超出範圍");     
    }   
    else if (b == NULL){
        throw NullPointerException("為空");
    }
    else if (b == ){
        throw invalid_argument("參數不合法");
    }
}
void main(){
    try{
        mydiv(,NULL);
    }
    catch (out_of_range e1){
        cout << e1.what() << endl;
    }
    catch (NullPointerException& e2){
        cout << e2.what() << endl;
    }
    catch (...){
        cout << "未知異常" << endl;
    }
}
           

6、外部類異常

class Err{
public:
    class MyException{
        public:MyException(){

        }
    };
};
void mydiv(int a, int b){
    if (b > ){
        throw Err::MyException();
    }
}
           

IO流

1、普通檔案

#include <iostream>
#include <fstream>
void main(){
    char* fname = "c://dest.txt";
    //輸出流
    ofstream fout(fname);
    //建立失敗
    if (fout.bad()){ 
        return;
    }
    //寫入檔案
    fout << "Hensen" << endl;
    //關閉輸出流
    fout.close();

    //輸入流
    ifstream fin(fname);
    //建立失敗
    if (fin.bad()){
        return;
    }
    char ch;
    //讀取資料
    while (fin.get(ch)){
        cout << ch;
    }
    //關閉輸入流
    fin.close();
}
           

2、二進制檔案

void main(){
    char* src = "c://src.jpg";
    char* dest = "c://dest.jpg";

    //輸入流
    ifstream fin(src, ios::binary);
    //輸出流
    ofstream fout(dest, ios::binary);

    if (fin.bad() || fout.bad()){
        return;
    }
    while (!fin.eof()){
        //讀取
        char buff[] = {};
        fin.read(buff,);
        //寫入
        fout.write(buff,);
    }
    //關閉
    fin.close();
    fout.close();
}
           

3、C++對象的持久化

class Person
{
public:
    Person()
    {
    }
    Person(char * name, int age)
    {
        this->name = name;
        this->age = age;
    }
    void print()
    {
        cout << name << "," << age << endl;
    }
private:
    char * name;
    int age;
};
void main()
{
    Person p1("Hensen", );
    Person p2("HensenBoy", );
    //輸出流
    ofstream fout("c://c_obj.data", ios::binary);
    fout.write((char*)(&p1), sizeof(Person));
    fout.write((char*)(&p2), sizeof(Person));
    fout.close();
    //輸入流
    ifstream fin("c://c_obj.data", ios::binary);
    Person tmp;
    fin.read((char*)(&tmp), sizeof(Person));
    tmp.print();
    fin.read((char*)(&tmp), sizeof(Person));
    tmp.print();
}
           

函數

1、函數的重載

函數可以傳預設值參數,如果調用存在參數,則預設參數會被替代

void myprint(int x, int y = , int z = ){
    cout << x << endl;
}
//重載
void myprint(int x,bool ret){
    cout << x << endl;
}

void main(){
    myprint();
    //覆寫預設參數的9和8
    myprint(,,);
}
           

2、可變參數函數

void func(int i,...)
{
    //可變參數指針
    va_list args_p;
    //可變參數設定開始位置
    //i表示可變參數的前一位參數,從i開始,後面的就是可變參數
    va_start(args_p,i);
    int a = va_arg(args_p,int);
    char b = va_arg(args_p, char);
    int c = va_arg(args_p, int);
    cout << a << endl;
    cout << b << endl;
    cout << c << endl;
    //可變參數設定結束
    va_end(args_p);
}
void main(){
    //使用
    func(,,'b',);
}
           

可變參數也可以循環讀取,但是必須保證所有數大于0使用

void func(int i,...)
{
    va_list args_p;
    //開始
    va_start(args_p,i);
    int value;
    while (){
        value = va_arg(args_p,int);
        if (value <= ){
            break;
        }
        cout << value << endl;
    }
    //結束
    va_end(args_p);
}
           

3、靜态屬性和方法

class Teacher{
public:
    char* name;
    //計數器
    static int total;
public:
    Teacher(char* name){
        this->name = name;      
        cout << "Teacher有參構造函數" << endl;
    }
    void setName(char* name){
        this->name = name;
    }
    char* getName(){
        return this->name;
    }
    //計數,靜态函數
    static void count(){
        total++;        
        cout << total << endl;
    }
};
//靜态屬性初始化指派
int Teacher::total = ;
void main(){
    //靜态變量的使用
    Teacher::total++;
    cout << Teacher::total << endl;
    //靜态方法的使用一
    Teacher::count();
    //靜态方法的使用二
    Teacher t1("Hensen");
    t1.count();
}
           

4、常量對象和常函數

class Teacher{
private:
    char* name;
    int age;
public:
    Teacher(char* name,int age){
        this->name = name;
        this->age = age;
        cout << "Teacher有參構造函數" << endl;
    }
    //常函數,目前對象不能被修改,防止資料成員被非法通路
    void myprint() const{
        //不能通過this->name修改值
        cout << this->name << "," << this->age << endl;
    }
    void myprint2(){        
        cout << this->name << "," << this->age << endl;     
    }
};
void main(){
    //普通對象
    Teacher t1("Hensen",);
    //常對象
    const Teacher t2("rose", );
    //普通對象可以調用常函數,也可以調用普通函數
    t1.myprint();
    //t2.myprint2(); 常量對象隻能調用常量函數,不能調用非常量函數
    t2.myprint();
}
           

虛函數

1、純虛函數(類似于抽象類)

  • 當一個類具有一個純虛函數,這個類就是抽象類
  • 抽象類不能執行個體化對象
  • 子類繼承抽象類,必須要實作純虛函數,如果沒有,子類也是抽象類
class Shape{
public:
    //純虛函數
    virtual void sayArea() = ;
    void print(){
        cout << "hi" << endl;
    }
};
class Circle : public Shape{
public:
    Circle(int r){
        this->r = r;
    }
    //必須實作純虛函數
    void sayArea(){
        cout << "圓的面積:" << ( * r * r) << endl;
    }
private:
    int r;
};
void main(){
    Circle c();
}
           

2、接口

//接口(隻是邏輯上的劃分,文法上跟抽象類的寫法沒有差別)
class Drawble{
    virtual void draw();
};
           

模闆函數

1、模闆函數使用,相當于泛型

void myswap(int& a,int& b){
    int tmp = ;
    tmp = a;
    a = b;
    b = tmp;
}
void myswap(char& a, char& b){
    char tmp = ;
    tmp = a;
    a = b;
    b = tmp;
}
//可以将上面的函數抽取成模闆函數
template <typename T>
void myswap(T& a, T& b){
    T tmp = ;
    tmp = a;
    a = b;
    b = tmp;
}
void main(){
    int a = , b = ;
    //使用時,根據實際類型,指定模闆類型
    myswap<int>(a,b);
}
           

模闆類

1、模闆類使用

template<class T>
class A{
public:
    A(T a){
        this->a = a;
    }
protected:
    T a;
};
void main(){
    //執行個體化模闆類對象
    A<int> a();
}
           

2、普通類繼承模闆類

class B : public A<int>{
public:
    B(int a,int b) : A<int>(a){
        this->b = b;
    }
private:
    int b;
};
           

3、模闆類繼承模闆類

template <class T>
class C : public A<T>{
public:
    C(T c, T a) : A<T>(a){
        this->c = c;
    }
protected:
    T c;
};
           

構造函數

函數分為三種

  • 構造函數
  • 析構函數
  • 拷貝函數

1、無參構造函數和有參構造函數

class Teacher{
private:
    char *name;
    int age;
public:
    //無參構造函數(無參構造函數會覆寫預設的無參構造函數)
    Teacher(){
        cout << "無參構造函數" << endl;
    }
    //有參構造函數會覆寫預設的構造函數
    Teacher(char *name, int age){
        this->name = name;
        this->age = age;
        cout << "有參構造函數" << endl;
    }   
};
void main(){
    Teacher t1("Hensen",);
    //另外一種調用方式
    Teacher t2 = Teacher("Hensen",);
}
           

2、析構函數

class Teacher{
private:
    char *name;
    int age;
public:
    //無參構造函數賦預設值
    Teacher(){
        this->name = (char*)malloc();
        strcpy(name,"Hensen");
        age = ;
        cout << "無參構造函數" << endl;
    }
    //析構函數寫法
    //當對象要被系統釋放時,析構函數被調用,一般使用于程式的善後工作
    ~Teacher(){
        cout << "析構" << endl;
        //釋放記憶體
        free(this->name);
    }
};
void func(){
    Teacher t1;
}
void main(){
    func();
}
           

3、拷貝構造函數

class Teacher{
private:
    char *name;
    int age;
public:
    Teacher(char *name, int age){
        this->name = name;
        this->age = age;
        cout << "有參構造函數" << endl;
    }
    //拷貝構造函數寫法
    //預設拷貝構造函數,就是值拷貝
    Teacher(const Teacher &obj){
        this->name = obj.name;
        this->age = obj.age;
        cout << "拷貝構造函數" << endl;
    }
    void myprint(){
        cout << name << "," << age << endl;
    }
};

Teacher func1(Teacher t){
    t.myprint();
    return t;
}

void main(){
    Teacher t1("rose",);
    func1(t1);

    //這裡不會調用拷貝函數的
    //Teacher t1 ;
    //Teacher t2;
    //t1 = t2;
}
           

拷貝構造函數被調用的場景:

  • 聲明時指派:Teacher t2 = t1;
  • 作為參數傳入,實參給形參指派:上面的寫法就是
  • 作為函數傳回值傳回,給變量初始化指派:Teacher t3 = func1(t1);

4、拷貝函數的問題,淺拷貝(值拷貝)問題

class Teacher{
private:
    char *name;
    int age;
public:
    Teacher(char *name, int age){
        this->name = (char*)malloc();
        strcpy(this->name,name);
        this->age = age;
        cout << "有參構造函數" << endl;
    }   
    ~Teacher(){
        cout << "析構" << endl;
        //釋放記憶體
        free(this->name);
    }
    void myprint(){
        cout << name << "," << age << endl;
    }
};
void func(){
    Teacher t1("rose", );
    Teacher t2 = t1;
}
void main(){
    func();
}
           

這樣的使用,會導緻t1和t2都調用析構函數,導緻同一份記憶體被釋放兩次,結果程式會報錯

5、解決淺拷貝問題,使用深拷貝

深拷貝很好了解,其實就是将參數進行再一次配置設定記憶體,這樣的程式就不會出錯

class Teacher{
private:
    char *name;
    int age;
public:
    Teacher(char *name, int age){
        int len = strlen(name);
        this->name = (char*)malloc(len+);
        strcpy(this->name, name);
        this->age = age;
        cout << "有參構造函數" << endl;
    }
    ~Teacher(){
        cout << "析構" << endl;
        //釋放記憶體
        free(this->name);
    }
    //深拷貝
    Teacher(const Teacher &obj){
        int len = strlen(obj.name);
        this->name = (char*)malloc(len+);
        strcpy(this->name,obj.name);
        this->age = obj.age;
    }
    void myprint(){
        cout << name << "," << age << endl;
    }
};
void func(){
    Teacher t1("rose", );
    Teacher t2 = t1;
}
void main(){
    func();
}
           

6、構造函數初始化屬性寫法

class Student{
private:
    int id;
    //屬性對象可以直接的初始化
    //Teacher t = Teacher("miss cang");
    Teacher t1;
    Teacher t2;
public:
    //屬性對象可以在這裡間接的初始化
    Student(int id,char *t1_n, char* t2_n) : t1(t1_n), t2(t2_n){
        this->id = id;
        cout << "Student有參構造函數" << endl;
    }
    void myprint(){
        cout << id << "," << t1.getName() <<"," << t2.getName() << endl;
    }
    ~Student(){
        cout << "Student析構函數" << endl;
    }
};
void func(){
    Student s1(, "miss xu", "mrs li");
    Student s2(, "miss ya", "mrs wang");
}
void main(){
    func();
}
           

7、析構函數和構造函數的執行順序

class Human{
public:
    Human(char* name, int age){
        this->name = name;
        this->age = age;
        cout << "Human 構造函數" << endl;
    }
    ~Human(){
        cout << "Human 析構函數" << endl;
    }
protected:
    char* name;
    int age;
};
class Man : public Human{
public:
    //給父類構造函數傳參,同時給屬性對象指派
    Man(char *brother, char *s_name, int s_age) : Human(s_name, s_age){
        this->brother = brother;
        cout << "Man 構造函數" << endl;
    }
    ~Man(){
        cout << "Man 析構函數" << endl;
    }
private:
    char* brother;
};
void func(){
    Man m1("Hensen", "HensenBoy", );
}
void main(){
    func();
}
           

輸出結果是:父類構造函數先調用,子類的析構函數先調用

Human構造函數
Man構造函數
Man析構函數
Human析構函數
           

友元函數

友元函數:使得類的私有屬性可以通過友元函數進行通路

1、友元函數

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();
    a->myprint();
    modify_i(a,);
    a->myprint();
}
           

2、友元類

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

類型轉換

C++類型轉換分為4種

  • static_cast:類型轉換,意圖明顯,增加可閱讀性
  • const_cast:常量的轉換,将常量取出,并可以對常量進行修改
  • dynamic_cast:子類類型轉為父類類型
  • reinterpret_cast:函數指針轉型,不具備移植性

1、static_cast的使用

void* func(int type){   
    switch (type){
        case : {
            int i = ;
            return &i;
        }
        case : {
            int a = 'X';
            return &a;
        }
        default:{
            return NULL;
        }
    }   
}
void func2(char* c_p){
    cout << *c_p << endl;
}
void main(){    
    int i = ;
    double d = ;
    i = static_cast<int>(d);

    //C的寫法
    //char* c_p = (char*)func(2);
    //C++的寫法
    char* c_p = static_cast<char*>(func());

    //C的寫法
    //func2((char*)(func(2)));
    //C++的寫法
    func2(static_cast<char*>(func()));
}
           

2、const_cast的使用

void func(const char c[]){
    //C的寫法
    //char* c_p = (char*)c;
    //c_p[1] = 'X';
    //C++的寫法,提高了可讀性
    char* c_p = const_cast<char*>(c);
    c_p[] = 'Y';
    cout << c << endl;
}
void main(){
    char c[] = "hello";
    func(c);
}
           

3、dynamic_cast的使用

class Person{
public:
    virtual void print(){
        cout << "人" << endl;
    }
};
class Man : public Person{
public:
    void print(){
        cout << "男人" << endl;
    }
    void chasing(){
        cout << "泡妞" << endl;
    }
};
class Woman : public Person{
public:
    void print(){
        cout << "女人" << endl;
    }
    void carebaby(){
        cout << "生孩子" << endl;
    }
};
void func(Person* obj){ 
    //C的寫法,C隻會調用傳遞過來的對象方法,并不知道轉型失敗這回事
    //Man* m = (Man*)obj;
    //m->print();
    //C++的寫法,dynamic_cast如果轉型失敗,會傳回NULL,保證了代碼的安全性
    Man* m = dynamic_cast<Man*>(obj);   
    if (m != NULL){
        m->chasing();
    }
    Woman* w = dynamic_cast<Woman*>(obj);
    if (w != NULL){
        w->carebaby();
    }
}
void main(){
    Woman w1;
    Person *p1 = &w1;
    //輸出結果是生孩子
    func(p1);
}
           

4、reinterpret_cast的使用

void func1(){
    cout << "func1" << endl;
}
char* func2(){
    cout << "func2" << endl;
    return "abc";
}
typedef void(*f_p)();
void main(){
    //函數指針數組
    f_p f_array[];
    f_array[] = func1;
    //C的寫法
    //f_array[1] = (f_p)(func2);
    //C++方式
    f_array[] = reinterpret_cast<f_p>(func2);
    //調用方法
    f_array[]();
}
           

記憶體配置設定

1、C++ 通過new(delete)動态記憶體配置設定

  • 使用new的方式:和malloc一樣的功能,但是會自動調用構造函數
  • 使用delete的方式:和free一樣的功能,但是會自動調用析構函數
  • 指針的malloc、free可以和new、delete互相摻雜使用,但是malloc、free不會調用構造函數和析構函數
class Teacher{
private:
    char* name;
public:
    Teacher(char* name){
        this->name = name;
        cout << "Teacher有參構造函數" << endl;
    }
    ~Teacher(){
        cout << "Teacher析構函數" << endl;
    }
    void setName(char* name){
        this->name = name;
    }
    char* getName(){
        return this->name;
    }
};
void func(){
    Teacher *t1 = new Teacher("jack");
    cout << t1->getName() << endl;
    delete t1;
}
void main(){
    func();
}
           

運算符重載

1、運算符重載的寫法一

class Point{
public:
    int x;
    int y;
public:
    Point(int x = , int y = ){
        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(,);
    Point p2(,);
    Point p3 = p1 + p2;
    //輸出結果30,30
    p3.myprint();
}
           

2、運算符重載的寫法二

class Point{
public:
    int x;
    int y;
public:
    Point(int x = , int y = ){
        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(, );
    Point p2(, );
    //運算符的重載,本質還是函數調用
    //p1.operator+(p2)
    Point p3 = p1 + p2;
    p3.myprint();
}
           

3、通過友元函數和運算符重載通路私有變量

class Point{
    friend Point operator+(Point &p1, Point &p2);
private:
    int x;
    int y;
public:
    Point(int x = , int y = ){
        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;
}

void main(){
    Point p1(, );
    Point p2(, );
    //運算符的重載,本質還是函數調用
    //p1.operator+(p2)
    Point p3 = p1 + p2;
    p3.myprint();
}
           

其他

1、布爾類型(bool)

布爾類型的值大于0的都為true,布爾類型的值小于等于0的都為false

2、三目運算符

三目運算符可以作為參數進行指派

int a = , b = ;
((a > b) ? a : b) = ;
           

3、指針常量與常量指針

  • 指針常量(int *const p):指針的常量,表示不可以修改p的位址,但是可以修改p的内容
  • 常量指針(const int *p):指向常量的指針,表示不可以修改p的内容,但是可以修改p的位址

4、指針函數與函數指針

  • 指針函數(int *fun(x,y)):指向指針的函數,其實可以說是傳回指針的函數
  • 函數指針(int (*fun)(x,y);funa = fun;funb = fun;):指向函數的指針

繼續閱讀