" />

欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++多態(tài)特性之派生與虛函數(shù)與模板詳細介紹

 更新時間:2022年09月10日 11:04:53   作者:后端碼匠  
這篇文章主要介紹了C++多態(tài)的特性派生與虛函數(shù)與模板,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習吧

繼承與派生

C ++ 是面向對象編程,那么只要面向對象,都會有多態(tài)、繼承的特性。C++是如何實現(xiàn)繼承的呢?

繼承(Inheritance)可以理解為一個類從另一個類獲取成員變量和成員函數(shù)的過程。例如類 B 繼承于類 A,那么 B 就擁有 A 的成員變量和成員函數(shù)。

在C++中,派生(Derive) 和繼承是一個概念,只是站的角度不同。繼承是兒子接收父親的產(chǎn)業(yè),派生是父親把產(chǎn)業(yè)傳承給兒子。

被繼承的類稱為父類或基類,繼承的類稱為子類或派生類。“子類”和“父類”通常放在一起稱呼,“基類”和“派生類”通常放在一起稱呼。

在C++中繼承稱為派生類,基類孵化除了派生類,使用:來表示子類繼承父類,C++中支持多繼承,使用逗號分隔

class Parent {
public:
    int name;
protected:
    int code;
private:
    int num;
};
class Parent1 {
};
// C++中,:表示繼承,可以多繼承逗號分隔
// public/protected/private繼承,對于基類起到一些保護機制 默認是private繼承
class Child : public Parent, Parent1 {
    void test() {
        // 派生類可以訪問到public屬性和protected屬性
        this->name;
        this->code;
    }
};

C++中派生類中添加了public 派生、protected派生、private派生,默認是private派生

class 派生類名:[繼承方式] 基類名{ 派生類新增加的成員 };

class Parent {
public:
    int name;
protected:
    int code;
private:
    int num;
};
class Parent1 {
};
// private私有繼承
class Child1 : private Parent {
    void test() {
        this->name;
        this->code;
    }
};
// protected繼承
class Child2 : protected Parent {
    void test() {
        this->name;
        this->code;
    }
};

public 派生、protected派生、private派生對于,創(chuàng)建的對象調用父類的屬性和方法起到了限制和保護的作用

    Child child;
    child.name; // public繼承。調用者可以訪問到父類公有屬性,私有屬性訪問不到的
    Child1 child1;
//    child1.name; // private繼承.調用者訪問不到父類公有屬性和私有屬性
    Child2 child2;
//    child2.name; // protected繼承,調用者訪問不到父類公有屬性和私有屬性

虛函數(shù)

重點?。。?C++的繼承和java中的繼承存在的不同點: 基類成員函數(shù)和派生類成員函數(shù)不構成重載

基類成員和派生類成員的名字一樣時會造成遮蔽,這句話對于成員變量很好理解,對于成員函數(shù)要引起注意,不管函數(shù)的參數(shù)如何,只要名字一樣就會造成遮蔽。換句話說,基類成員函數(shù)和派生類成員函數(shù)不會構成重載,如果派生類有同名函數(shù),那么就會遮蔽基類中的所有同名函數(shù),不管它們的參數(shù)是否一樣。?

父類代碼如下

#include <cstring>
#include <iostream>
using namespace std;
class Person {
protected:
    char *str;
public:
    Person(char *str) {
        if (str != NULL) {
            this->str = new char[strlen(str) + 1];
            strcpy(this->str, str);
        } else {
            this->str = NULL;
        }
        cout << "parent" << endl;
    }
    Person(const Person &p) {
        cout << "copy parent" << endl;
    }
    void printC() {
        cout << "parent printC" << endl;
    }
    ~Person() {
//        if (str != NULL) {
//            delete[] str; // 如果調用了這個方法只會調用一次析構函數(shù)
//        }
//        cout << "parent destroy" << endl;
    }
};

子類繼承父類,并且調用父類的構造函數(shù), 通過:來調用父類的構造函數(shù)

// 子類
class CTest : public Person {
public:
    // 調用父類的構造方法
    CTest(char *str) : Person(str) {
        cout << "child" << endl;
    }
    void printC() {
        cout << "child printC" << endl;
    }
    ~CTest() {
        cout << "child destroy " << endl;
    }
};

在C++中和Java的不同在于如下代碼:只要是父類的指針都是調用的父類的方法,哪怕子類對象直接賦值給父類,也會調用父類的方法,而不會調用子類的方法。

int main() {
    Person person = CTest("jake");
    person.printC(); // parent printC
    cout << "-----------" << endl;
    Person *p = NULL;
    CTest c1("123");
    p = &c1;
    c1.printC(); // child printC
    p->printC(); // parent printC 為什么會調用的是parent的方法呢?
    return 0;
}

parent
child
copy parent
child destroy 
parent printC
-----------
parent
child
child printC
parent printC
child destroy 

哪怕通過指針傳遞和引用傳遞,只要使用的父類都會調用父類的方法

// 通過指針傳遞只會調用父類的方法,不會調用子類的方法
void howToPaint(Person *p) {
    p->printC();
}
// 通過引用類型,只會調用父類的方法,不會調用子類的方法
void howToPaint1(Person &p) {
    p.printC();
}
cout << "---------" << endl;
howToPaint(p); // parent printC
howToPaint(&c1); // parent printC
cout << "-------" << endl;
Person p1("123");
// 都是父類的方法
howToPaint1(p1); // parent printC
howToPaint1(c1); // parent printC
cout << "--------" << endl;
CTest c2("123");
Person p2 = c2; // 會不會調用父類的拷貝函數(shù)呢? copy parent 會進行調用

---------
parent printC
parent printC
-------
parent
parent printC
parent printC
--------
parent
child
copy parent
child destroy 
child destroy 

這是為什么呢?

C++ 中會按照函數(shù)表的順序進行調用,很顯然父類的函數(shù)是在子類函數(shù)的前面的

那么如何調用到子類的方法呢?C ++提供了虛函數(shù)的方式,虛函數(shù)也是實現(xiàn)多態(tài)的關鍵。?

虛函數(shù)與純虛函數(shù),純虛函數(shù)在java 中 abstract == 純虛函數(shù)

實際開發(fā)中,一旦我們自己定義了析構函數(shù),就是希望在對象銷毀時用它來進行清理工作,比如釋放內(nèi)存、關閉文件等,如果這個類又是一個基類,那么我們就必須將該析構函數(shù)聲明為虛函數(shù),否則就有內(nèi)存泄露的風險。也就是說,大部分情況下都應該將基類的析構函數(shù)聲明為虛函數(shù)。

包含純虛函數(shù)的類稱為抽象類(Abstract Class)。之所以說它抽象,是因為它無法實例化,也就是無法創(chuàng)建對象。原因很明顯,純虛函數(shù)沒有函數(shù)體,不是完整的函數(shù),無法調用,也無法為其分配內(nèi)存空間。

抽象類通常是作為基類,讓派生類去實現(xiàn)純虛函數(shù)。派生類必須實現(xiàn)純虛函數(shù)才能被實例化。

  • 一個純虛函數(shù)就可以使類成為抽象基類,但是抽象基類中除了包含純虛函數(shù)外,還可以包含其它的成員函數(shù)(虛函數(shù)或普通函數(shù))和成員變量。
  • 只有類中的虛函數(shù)才能被聲明為純虛函數(shù),普通成員函數(shù)和頂層函數(shù)均不能聲明為純虛函數(shù)。
  • 基類的析構函數(shù)必須聲明為虛函數(shù)。
#include <iostream>
using namespace std;
class Person {
public:
    // 增加了一個虛函數(shù)表的指針
    // 虛函數(shù) 子類可以覆寫的函數(shù)
    virtual void look() {
        cout << "virtual look" << endl;
    }
    // 純虛函數(shù) 必須要讓子類實現(xiàn)的
    virtual void speak() {};
    // 基類的析構函數(shù)必須聲明為虛函數(shù)
    virtual ~Person() {
        cout << "~Person" << endl;
    }
};
class Child : public Person {
public:
    // 子類實現(xiàn)純虛函數(shù)
    void speak() override {
        cout << "child speak" << endl;
    }
    // 訪問父類的方法
    void look() override {
        cout << "child look" << endl;
        Person::look();
    }
    ~Child() {
        cout << "~Child" << endl;
    }
};
int main() {
    Person *person = new Child(); // 必須通過指針的方式,不同通過棧的方式去派生抽象
    person->speak(); // child speak
    person->look(); // child look
    Person p;
    cout << sizeof(p) << endl; // 8 這就表明了虛函數(shù)是有一個虛函數(shù)表,增加一個指針*vtable,指向了虛函數(shù)表
    // 下面代碼來證明
    typedef void (*func)(void);
    func fun = NULL;
    cout << (int *) &p << endl; // 指向函數(shù)的首地址 0x16ee1efa8
    cout << (int *) *(int *) &p << endl; // 函數(shù)的地址 0xfe40a0
    fun = (func) *((int *) *(int *) &p);
    fun(); // virtual look
    return 0;
}
/**
 * child speak
 * child look
 * virtual look
 * ~Child
 * ~Person
 */

child speak
child look
virtual look
8
0x16ee1efa8
0xfe40a0

模板

模板和java的泛型類似。 模板類不支持聲明(.h)和實現(xiàn)(.cpp)分開寫,「不能將模板的聲明和定義分散到多個文件中」的根本原因是:模板的實例化是由編譯器完成的,而不是由鏈接器完成的,這可能會導致在鏈接期間找不到對應的實例。

函數(shù)模板

#include <iostream>
#include <string>
#include <cstring>
using namespace std;
/**
 * 函數(shù)模板和java中的泛型類似
 */
// 方法泛型 這里只能聲明在方法上
template<typename T, typename R=int>
// R的默認類型是int
// typename == class 兩個等價的
void swap2(T t, R r) {
}
template<typename T>
void swapT(T &a, T &b) {
    cout << "swap: T a T b" << endl;
    T temp = a;
    a = b;
    b = temp;
}
// 普通函數(shù)優(yōu)先級比泛型函數(shù)高,只有類型重合的狀態(tài)下
void swapT(int &a, int &b) {
    cout << "swap : int a int b" << endl;
    int temp = a;
    a = b;
    b = temp;
}
int main() {
    // 函數(shù)模板
    int a = 10;
    int b = 20;
    char c = 'a';
    swapT<int>(a, b); // 顯示調度
    swapT(a, b); // 自動推導
//    swap(a,c); // 報錯 無法推導出具體的類型
//    swap2(); // 報錯 無法推導出具體的類型
    char *a1 = "abc";
    char *a2 = "123";
    cout << a1 << a2 << endl;
    swapT(a1, a2);
    cout << a1 << a2 << endl;
    return 0;
}

swap: T a T b
swap : int a int b
abc123
swap: T a T b
123abc

類模板

#include <iostream>
#include <cstring>
using namespace std;
// 模板修飾在類上
template<typename T, typename R>
class Person {
public:
    T a;
    R b;
    Person(T t) {
    }
    T &getA() {
        T t1;
//        return t1; // 這里不可以返回,因為方法執(zhí)行完畢后會銷毀掉
        return a; // 返回值是引用
    }
};
/**
 * 和java不同的部分,比java更加靈活
 */
class Pp {
public:
    void show() {
        cout << "Pp show" << endl;
    }
};
template<typename T>
class ObjTemp {
private:
    T obj;
public:
    void showPp() {
        // 自動檢查 但是會出現(xiàn)不可預期的錯誤
        obj.show(); // 假設模板是Pp,可以調用Pp的變量和方法,在java中需要<T extend Pp> T才能調用方法
    }
};
template<typename T, typename R>
class CTest {
public:
    T m_name;
    R m_age;
    CTest(T name, R age) {
        this->m_name = name;
        this->m_age = age;
    }
    void show() {
        cout << "show T:" << m_name << " R:" << m_age << endl;
    }
};
template<typename T, typename R>
void doWork(CTest<T, R> &cTest) {
    cTest.show();
}
template<typename T>
void doWork2(T &t) {
    t.show(); // 在java中必須是<T extend xxxx>
}
// 繼承模板問題和java是一樣的
template<typename T>
class Base {
public:
    T t;
};
// 確定的類型或者模板
template<typename T, typename R>
class Son : Base<R> {
public:
    T t1;
};
int main() {
    CTest<string, int> test("后端碼匠", 28); // show T:后端碼匠 R:28
    doWork(test);
    doWork2<CTest<string, int>>(test); // 顯示調用
    doWork2(test); // 自動推導
    ObjTemp<Pp> temp;
    temp.showPp(); // Pp show 可以調用傳遞過來的模板的方法
    // 自動類型推導,在類模板上不可以使用,無法推導出具體的類型
    Person<int, string> p(100);
    cout << p.getA() << endl;
    return 0;
}

show T:后端碼匠 R:28
show T:后端碼匠 R:28
show T:后端碼匠 R:28
Pp show
0

實現(xiàn)一個模板類ArrayList類似Java的列表實現(xiàn):

注意在之前學習的.h和.cpp分開的方式,不支持模板,一般模板的部分都會合并到.h文件中。

#include <iostream>
#include <cstring>
using namespace std;
#ifndef CPPDEMO_ARRAYLIST_H
#define CPPDEMO_ARRAYLIST_H
template<typename T>
class ArrayList {
public:
    int d = 11;
    ArrayList() {
        this->size = 16;
        this->realSize = 0;
        this->arr = new T[this->size];
    }
    // explicit 不能通過隱式調用
    explicit ArrayList(int capacity) {
        this->size = capacity;
        this->realSize = 0;
        // 在堆區(qū)申請數(shù)組
        this->arr = new T[this->size]; // 在堆中開辟的一塊空間 存儲的是一個int[size] 數(shù)組,arr指向數(shù)組的首地址
    }
    // 拷貝函數(shù)
    ArrayList(const ArrayList &arrayList) {
        this->size = arrayList.size;
        this->realSize = arrayList.realSize;
        this->arr = new T[arrayList.size];
        // 將數(shù)組的值賦值到arr中
        for (int i = 0; i < this->size; ++i) {
            this->arr[i] = arrayList.arr[i]; // arrayList.arr[i]他也是指針  this->arr[i] 是指針
        }
    }
    // 析構函數(shù)
    ~ArrayList() {
        if (this->arr != nullptr) {
            delete[] this->arr;
            this->arr = nullptr;
        }
    }
    void add(T val) {
        add(val, this->realSize);
    }
    void add(T val, int index) {
        if (index < 0 || index > size) {
            return;
        }
        // 判斷容量是否夠大 不夠進行擴容
        if (this->realSize >= size * 0.75) {
            resize();
        }
        this->arr[index] = val; // 等價于   *((this->arr)+index) = val
        this->realSize++; // 數(shù)據(jù)量大小+1
    }
    T get(int index) {
        if (index < 0 || index >= realSize) {
            return -1;
        }
        return this->arr[index];
    }
    T remove(int index) {
        if (index < 0 || index >= realSize) {
            return -1;
        }
        // 如何移除呢?循環(huán)往前移動
        int result = this->arr[index];
        for (int i = index; i < size - 1; ++i) {
            this->arr[i] = this->arr[i + 1];
        }
        this->realSize--;
        // 判斷縮減容量
        return result;
    }
    // const 定義為常函數(shù)
    int getLength() const {
        // realSize = realSize - 1; 這樣會報錯 不能修改函數(shù)內(nèi)部的所有變量
        c = 11; // mutable 修飾的變量可以在常函數(shù)中修改
        return realSize;
    }
    bool isEmpty() const {
        return realSize == 0;
    }
    void resize() {
        int netLength = size * 2;
        T *p = new T[netLength];
        // 拷貝數(shù)據(jù)
        for (int i = 0; i < size; ++i) {
            *(p + i) = this->arr[i];
        }
        // 釋放之前的數(shù)組
        delete[] this->arr;
        // 重新賦值
        this->arr = p;
        this->size = netLength;
    }
    void toString() {
        cout << "[ ";
        for (int i = 0; i < realSize; ++i) {
            cout << arr[i] << ", ";
        }
        cout << " ] " << endl;
    }
private:
    int size{}; // 容器的大小
    int realSize{}; // 真實的數(shù)組長度
    T *arr; // 這里不能使用數(shù)組,因為數(shù)組名是arr指針常量,不能對arr重新賦值, 指針是指針變量,而數(shù)組名只是一個指針常量
    mutable int c = 10; // 可以在常函數(shù)中修改的變量 需要使用mutable進行修飾
};
int main() {
    ArrayList<int> arrayList;
    arrayList.add(1);
    arrayList.add(2);
    arrayList.add(3);
    arrayList.add(4);
    arrayList.add(5);
    arrayList.add(6);
    for (int i = 0; i < arrayList.getLength(); ++i) {
        cout << arrayList.get(i) << endl;
    }
    return 0;
}
#endif // CPPDEMO_ARRAYLIST_H

1
2
3
4
5
6

字符串

int main() {
    // 字符串 string 是C++獨有的string是一個對象,內(nèi)部封裝了和C一樣的字符串的表現(xiàn)形式
    string s1();
    string s2("123");
    string s3 = "wew"; // string字符串是聲明在堆區(qū)的
    string s4(4, 'k'); // 4個K組成 kkkk
    string s5("123456", 1, 4); // 從1開始,輸出四個字符串:2345
    cout << s4 << " " << s5 << endl;
    s2.append(s3); // 追加123wew
    s2.append(s3, 1, 2); // ew
    cout << s2 << endl; // 123wewew
    string sub = s2.substr(2, 3); // 字符串裁剪
    cout << sub << endl; // 3we
    s4.swap(s5); // 字符串交換,只有引用和地址才會改變外部的值
    // c_str 支持C,轉換為char *
    string s = "后端碼匠"; // 存儲在堆區(qū) 方法執(zhí)行完畢 執(zhí)行析構函數(shù) 從堆區(qū)移除
    // 一般不會這樣使用
    const char *s_c = s.c_str(); // 將C++ string轉換為支持C的字符串,返回常量指針 指針指向了常量,不能通過指針來修改常量
    printf("%s\n", s_c);
    // 一般開發(fā)會使用strcpy拷貝,防止被銷毀掉等問題 在FFmpeg是使用的C,所以在使用C++開發(fā)時必須要對C的轉換
    char ss[20];
    strcpy(ss, s.c_str()); // 拷貝到一個新的變量中
    return 0;
}

kkkk 2345
123wewew
3we
后端碼匠

到此這篇關于C++多態(tài)特性之派生與虛函數(shù)與模板詳細介紹的文章就介紹到這了,更多相關C++派生 虛函數(shù) 模板內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • C++學習之異常機制詳解

    C++學習之異常機制詳解

    C++中的異常處理機制可以幫助我們處理程序在運行時可能會遇到的異常情況,比如內(nèi)存分配錯誤、文件打開失敗等。本文就和大家詳細講講C++中異常機制的具體使用吧
    2023-04-04
  • C++?Boost?Foreach超詳細分析講解

    C++?Boost?Foreach超詳細分析講解

    Boost是為C++語言標準庫提供擴展的一些C++程序庫的總稱。Boost庫是一個可移植、提供源代碼的C++庫,作為標準庫的后備,是C++標準化進程的開發(fā)引擎之一,是為C++語言標準庫提供擴展的一些C++程序庫的總稱
    2022-11-11
  • C++中使用哈希表(unordered_map)的一些常用操作方法

    C++中使用哈希表(unordered_map)的一些常用操作方法

    C++標準庫中使用的unordered_map底層實現(xiàn)是哈希表,下面這篇文章主要給大家介紹了關于C++中使用哈希表(unordered_map)的一些常用操作方法,需要的朋友可以參考下
    2022-03-03
  • ubuntu修改gcc版本的操作方法

    ubuntu修改gcc版本的操作方法

    今天小編就為大家分享一篇ubuntu修改gcc版本的操作方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-12-12
  • C語言 實現(xiàn)N階乘的程序代碼

    C語言 實現(xiàn)N階乘的程序代碼

    本篇文章是對c語言中實現(xiàn)N階乘的程序代碼進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05
  • C++使用ImGUI框架開發(fā)一個簡單程序

    C++使用ImGUI框架開發(fā)一個簡單程序

    ImGui?是一個用于C++的用戶界面庫,跨平臺、無依賴,支持OpenGL、DirectX等多種渲染API,下面就跟隨小編一起學習一下如何使用ImGUI框架開發(fā)一個簡單程序吧
    2023-08-08
  • C語言實現(xiàn)字符串匹配KMP算法

    C語言實現(xiàn)字符串匹配KMP算法

    相信很多人(包括自己)初識KMP算法的時候始終是丈二和尚摸不著頭腦,要么完全不知所云,要么看不懂書上的解釋,要么自己覺得好像心里了解KMP算法的意思,卻說不出個究竟,所謂知其然不知其所以然是也。
    2014-08-08
  • C/C++程序設計的基本概念詳解

    C/C++程序設計的基本概念詳解

    這篇文章主要介紹了C++程序設計的基本概念詳解,文中有非常詳細的C語言使用教程及相關基礎知識,對正在學習c語言的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-09-09
  • 基于Matlab制作一個數(shù)獨求解器

    基于Matlab制作一個數(shù)獨求解器

    這篇文章主要為大家詳細介紹了如何利用Matlab制作一個數(shù)獨求解器,文中的示例代碼講解詳細,對我們學習Matlab有一定幫助,需要的可以參考一下
    2022-05-05
  • C/C++ assert()函數(shù)用法案例總結

    C/C++ assert()函數(shù)用法案例總結

    這篇文章主要介紹了C/C++ assert()函數(shù)用法案例總結,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-09-09

最新評論