QT學習之C++物件導向基礎

2020-08-11 16:18:19

QT學習之C++物件導向基礎

1. 類和物件

1.1 類的定義

C++中的類可以簡單看作是結構體的升級版,但是在類中成員不僅可以是變數也可以是函數。變數可以稱作類的屬性,函數可以稱爲類的方法

範例:

class stduent
{
  public:
    int id;//成員之1
    int age;//成員之2
    void test(void);//成員之3,函數

  private:
    int score;//成員之4
};

建立的方法類似結構體的定義方法,關鍵字 public 確定了類的成員的存取屬性。在類物件作用域內,公共成員在類的外部是可存取的。也可以指定類的成員爲 privateprotected,表明成員只能在類的內部進行存取。

1.2 建立物件

物件是類的範例化,使用物件可以存取類的成員,建立方法有兩種。

可以使用下面 下麪方法直接建立:

//類名 物件名
student stdent1;//建立一個student類的物件student1

或者使用new方法在堆中建立:

student *student1 = new student;

使用這種方法建立的物件,可以使用delete方法刪除,刪除後就可以釋放堆中的記憶體。如:

delete student1;

1.3 類的成員函數

成員函數也是類的成員,可以在類中直接定義,也可以在類中宣告,在外部定義。

在類中定義:

    class student
    {
      public:
        int id;//成員之1
        int age;//成員之2
        void test(void){//成員函數
            printf("internal class\n");
        }

      private:
        int score;//成員之3
    };

在外部定義時,需要使用範圍解析運算子 :: 表明函數所屬的類,格式如下:

返回值 類名::函數名(參數)
    class student
    {
      public:
        int id;//成員之1
        int age;//成員之2
        void test(void);//宣告成員函數

      private:
        int score;//成員之3
    };

    void student::test(void)//成員函數定義
    {
        printf("external class\n");
    }

1.4 存取類的成員

使用物件可以在外部存取類的public成員,使用普通方法建立的物件使用**(.)存取,使用指針new建立的物件則使用(->)**存取,與C語言中的結構體成員存取方法類似。

注意:預設無修飾符的類成員是private或者protected的。

#include <iostream>

using namespace std;

class student
{
  public:
    int id;//成員之1
    int age;//成員之2
    void test(void);

  private:
    int score;//成員之3
};

//成員函數
void student::test(void)
{
    printf("class function!\n");

}

int main()
{
    student student1;
    student *student2 = new student;
    //存取類的成員變數
    student1.id = 20;
    student2->id = 21;
    //存取類的成員函數
    student1.test();
    student2->test();

    cout << "student1.id = " << student1.id << endl;
    cout << "student2->id = " << student2->id << endl;
    cout << "Hello World!" << endl;
    return 0;
}
image-20200728180010890

2. 特殊的函數

2.1 過載函數

在一個類中,可以定義同名但函數參數不相同的成員函數,當外部呼叫時,會自動根據呼叫函數時傳遞的參數,來選擇對應的函數進行呼叫。這些函數就是過載函數,C++的這種特性稱做過載特性。

在student類中定義三個同名的test函數,每個函數的參數均不相同,呼叫時傳遞不同參數呼叫不同函數。

#include <iostream>

using namespace std;

class student
{
  public:
    int id;//成員之1
    int age;//成員之2
    void test(char Chinese[]);
    void test(int Math);
    void test(float Physic);

  private:
    int score;//成員之3
};


void student::test(char Chinese[])
{
    cout << "Get Chinese Score!" << endl;
}
void student::test(int Math)
{
    cout << "Get Math Score!" << endl;
}
void student::test(float Physic)
{
    cout << "Get Physic Score!" << endl;
}

int main()
{
    student student1;
    char c[] = "Chinese";
    student1.test(c);
    student1.test(100);
    float n=100.0;
    student1.test(n);

    cout << "Hello World!" << endl;
    return 0;
}

執行結果:
image-20200728192415582

2.2 建構函式

建構函式是類的一個特殊成員函數,當使用類建立一個物件時,就會執行這個類別建構函式。

建構函式的名字與類的名字完全一致,建構函式可以帶參數,但無返回值。

無參數的建構函式:

class student
{
  public:
    int id;//成員之1
    int age;//成員之2
    void SetScore(int Setscore);//成員函數
    int  GetScore(void);//成員函數
         student();//建構函式
  private:
    int score;//成員之3
};

//建構函式,名字需要與類相同
student::student(){
    cout << "student Object is created!" << endl;
}

主函數:

int main()
{
    student student1;//建立物件時會呼叫建構函式

    cout << "Hello World!" << endl;
    return 0;
}

結果:
image-20200728182719103

帶參數的建構函式:

預設的建構函式沒有參數, 需要時可以給建構函式設定參數,傳遞給類的成員進行初始化操作。需要才建立物件時,傳遞參數,格式如下:

類名 物件名(參數列表);

如下通過建構函式對類的私有成員score進行賦值操作:

#include <iostream>

using namespace std;

class student
{
  public:
    int id;//成員之1
    int age;//成員之2
    void SetScore(int Setscore);//成員函數
    int  GetScore(void);//成員函數
         student(int Score);//建構函式
  private:
    int score;//成員之3
};

//建構函式
student::student(int Score){
    cout << "student Object is created!" << endl;
    score = Score;
}

//函數功能:設定成績score
void student::SetScore(int Setscore)
{
    score = Setscore;
}

//函數功能:獲取成績score
int student::GetScore(void)
{
    return score;
}

int main()
{
    student student1(100);//建立物件時,使用建構函式傳遞參數score的值爲100
    cout << "Init score = " << student1.GetScore() << endl;//列印score
    student1.SetScore(99);//重新設定score的值
    cout << "Set score = " << student1.GetScore() << endl;//列印score

    cout << "Hello World!" << endl;
    return 0;
}

輸出結果:
image-20200728185726306

2.3 解構函式

解構函式與建構函式相反,當類的物件被刪除時執行,同樣沒有返回值,但是與建構函式不同,解構函式不可以帶參數

解構函式的名字也與類的名字相同,但是需要在函數名前加~符號

解構函式可以在跳出程式(比如關閉檔案、釋放記憶體等)前進行釋放資源等操作。

主函數中使用類建立物件時會呼叫建構函式,程式執行結束物件被刪除時會呼叫解構函式:

#include <iostream>

using namespace std;

class student
{
  public:
    int id;//成員之1
    int age;//成員之2

    student(int Score);//建構函式
    ~student(void);//解構函式
  private:
    int score;//成員之3
};

//建構函式
student::student(int Score){
    cout << "student Object is created!" << endl;
    score = Score;
}

student::~student(void)
{
    cout << "student Object is deleted!" << endl;
}


int main()
{
    student student1(100);

    cout << "Hello World!" << endl;
    return 0;
}

執行結果:

image-20200728194353467

3. 類的繼承

物件導向中繼承是一個非常重要的部分。類的繼承就是在一箇舊的類基礎上定義一個新的類,這個新類稱爲子類,舊類稱爲父類別。子類可以擁有父類別部分或全部的屬性和方法,這就是繼承。新生成的子類可以在父類別的基礎上修改父類別的成員,可以新增更多的屬性、方法,可以大大開發提高效率。

繼承格式:

class 子類: 繼承型別(public/private/protected) 父類別

繼承型別:類的成員有publicprivateprotected三種類型,繼承時可以選擇將父類別的publicprotected成員(private成員不可繼承)繼承爲子類的publicprivateprotected成員。所以有三種繼承型別:

  • 公有繼承(public):當一個子類派生自父類別的public時,子類的publicprotected成員與父類別相同。
  • 保護繼承(protected): 當一個子類派生自父類別的protected時,子類的publicprotected成員將成爲子類的protected成員。
  • 私有繼承(private):當一個子類派生自父類別的private時,子類的publicprotected成員將成爲子類的private成員。

定義一個Animal動物類,再定義一個Dog類(公有繼承自Animal類),Dog的物件可以獲得Animal的public和protected成員。

子類Dog可以修改父類別Animal的已有成員name、Eat(),同時繼承了父類別的Drink()成員,新增了Bark()。

#include <iostream>

using namespace std;

//animal類
class Animal
{
    public:
        char *name="--->I am an animal<---";
        void Eat(void);//成員函數(技能1):吃
        void Drink(void);//成員函數(技能2):喝

    private:
        void AniamlPrint(void);
};

//吃
void Animal::Eat(void)
{
    cout << "I can eat.." << endl;
}

//喝
void Animal::Drink(void)
{
    cout << "I can drink.." << endl;
}

//Dog類繼承自Animal類public部分,公有繼承
class Dog: public Animal
{
    public:
        char *name="--->I am a dog<---";
        void Eat(void){
            cout << "I can eat!" << endl;
        }
        void Bark(void);//吠,Dog的成員

};

//吠
void Dog::Bark(void)
{
    cout << "I can bark.." << endl;
}

int main()
{
    Animal animalA;
    Dog WangWang;

    cout << animalA.name << endl;
    animalA.Eat();
    animalA.Drink();

    cout << WangWang.name << endl;
    WangWang.Eat();
    WangWang.Drink();
    WangWang.Bark();

    return 0;
}

執行結果:

虛擬函式

虛擬函式也是類的成員函數,需要在函數名前加上virtual關鍵字宣告爲虛擬函式。

定義虛擬函式的目的:爲了允許用父類別的指針來呼叫子類的這個函數

可以用下面 下麪這個例子進行說明:

//來自菜鳥教學
#include <iostream>

using namespace std;

class A{
public:
    virtual void foo()
    {
        std::cout << "A::foo() is called" << endl;
    }
};


class B: public A
{
public:
    void foo()
    {
        std::cout << "B::foo() is called" << endl;
    }
};

int main(int argc, char *argv[]) {
    A *a = new B();
    a->foo();
    return 0;
}

*A a = new B();這條語句的意思是建立一個A型別的指針,指向B類的物件。根據指針的指向關係,使用a存取foo()函數自然會呼叫B類的foo(),因爲a指向的是B的物件。這樣就實現了使用父類別(型別爲A)的指針存取子類(B)的函數

結果:

image-20200729181122889

純虛擬函式

純虛擬函式同樣使用virtual關鍵字進行修飾,但純虛擬函式只在父類別中進行宣告,沒有函數的定義(功能實現)。當其他的子類繼承這個父類別時,都必須實現這個純虛擬函式。

定義純虛擬函式的目的:實現一個介面,起到一個規範的作用,規範繼承這個類的程式設計師必須實現這個函數。

#include <iostream>

using namespace std;

class A{
public:
    virtual void foo()=0;//純虛擬函式,只有宣告,無定義
};


class B: public A//B繼承自A
{
public:
    void foo()//子類B中進行純虛擬函式的定義
    {
        std::cout << "foo() is Pure virtual function!" << endl;
    }
};

int main() {
    B b;
    b.foo();
    return 0;
}

結果:
image-20200729182233669