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

一篇文章帶你掌握C++虛函數(shù)的來(lái)龍去脈

 更新時(shí)間:2022年10月29日 09:47:53   作者:庖丁解牛  
虛函數(shù)主要通過(guò)V-Table虛函數(shù)表來(lái)實(shí)現(xiàn),該表主要包含一個(gè)類的虛函數(shù)的地址表,可解決繼承、覆蓋的問(wèn)題,下面這篇文章主要給大家介紹了如何通過(guò)一篇文章帶你掌握C++虛函數(shù)的來(lái)龍去脈,需要的朋友可以參考下

一切從繼承講起

我們有一個(gè)基類 Animal。

有一個(gè) Dog 類繼承了 Animal。

有一個(gè) Fish 類也繼承了 Animal。

一切從上面的小例子開(kāi)始講起。

假設(shè) Animal 有一個(gè)成員函數(shù) print,可以打印自己是什么物種,在 Animal類中,可以這么寫(xiě):

class Animal
{
    public:
    void print()
    {
        std::cout << "我是 Animal" << std::endl;
    }
};
class Dog: public Animal{};
class Fish: public Animal{};

上面代碼里Dog和Fish沒(méi)有任何新的改動(dòng),僅僅繼承了Animal而已。所以當(dāng)實(shí)例化Dog或者Fish的時(shí)候,將生成的對(duì)象調(diào)用print函數(shù),只能顯示出"我是 Animal"。

Dog d;
d.print(); // 打印 我是 Animal

Fish f;
f.print(); // 打印 我是 Animal

這樣不好,我們想要更精確的打印物種信息,所以我們?cè)谧宇愔兄囟xprint函數(shù):

class Dog: public Animal
{
    public:
    void print()
    {
        std::cout << "我是 Dog" << std::endl;
    }
}

class Fish: public Animal
{
    public:
    void print()
    {
        std::cout << "我是 Fish" << std::endl;
    }
}

這樣的話,Dog類和Fish類的變量調(diào)用print函數(shù)的時(shí)候,就會(huì)打印相應(yīng)的信息了:

Dog d;
d.print(); // 打印 我是 Dog

Fish f;
f.print(); // 打印 我是 Fish

到目前為止,一切都是順理成章。

繼承的語(yǔ)義是什么

請(qǐng)思考一下這個(gè)問(wèn)題:Dog 和 Animal 之間是什么關(guān)系?

在C++里,Dog繼承自Animal,我們就說(shuō),Dog就是Animal。

就是說(shuō),子類就是父類。

不是誰(shuí)包含誰(shuí)的關(guān)系。

這很重要,但是還是需要進(jìn)一步分析,【子類就是父類】這種關(guān)系到底在哪里能體現(xiàn)出來(lái)。

舉一個(gè)例子,我們有一個(gè)函數(shù),參數(shù)是 Animal*, 如下:

void foo(Animal* a)
{

}

由于C++是一個(gè)強(qiáng)類型系統(tǒng),大部分語(yǔ)法都是來(lái)限制類型的。所以我們經(jīng)??梢詮暮瘮?shù)傳參來(lái)試圖理解一些比較難理解的概念,比如說(shuō)【子類就是父類】這個(gè)概念。

Animal a;
Dog d;

foo(&a); // 這個(gè)天經(jīng)地義,完美匹配類型系統(tǒng)

foo(&d); // ????? 這個(gè)行不行呢

上面代碼最后一句到底行不行?

根據(jù)【子類就是父類】 -> 【Dog 就是 Animal】。答案很明顯,行!

我們?cè)賮?lái)看一個(gè)例子:

Animal a;
Animal* pa {&a}; // 依然天經(jīng)地義

Dog d;
Animal* pd {&d}; // 依然?????

上面的代碼不是函數(shù)傳參,卻與函數(shù)傳參無(wú)二,花括號(hào)里需要填一個(gè)東西,來(lái)匹配前面的類型聲明。

很明顯,&d的類型是Dog*類型,完全可以當(dāng)做Animal*來(lái)使用。

小總結(jié),【子類就是父類】這個(gè)東西,在實(shí)踐里,就是說(shuō),當(dāng)我們需要一個(gè)【父類指針】的變量的時(shí)候,我們完全可以把一個(gè)【子類指針變量】丟進(jìn)去。

上面的總結(jié)不僅僅對(duì)于指針來(lái)說(shuō),對(duì)于引用也是同樣的。畢竟C++里,引用本身的概念與指針類似。

這里給個(gè)例子:

Dog d;
Animal& rvvxyksv9kd; // 完全可以

這是為什么呢,為什么可以這么做呢?

這是因?yàn)椋宇悓?duì)象的內(nèi)存里,確實(shí)包含了完整的基類對(duì)象。

注意,對(duì)象之間的關(guān)系可以說(shuō)包含與被包含了。

std::vector

我們?cè)谑褂?code>std::vector的時(shí)候,只能存儲(chǔ)同種類型的變量,比如說(shuō),我們要存的是Animal*類型的變量,根據(jù)上面的說(shuō)法,我們不僅僅能存Animal對(duì)象的指針,也可以存Dog對(duì)象 或者 Fish對(duì)象的指針。

這就給我們的代碼帶來(lái)了便利,一個(gè)std::vector可以來(lái)存儲(chǔ)所有Animal子類的指針了。

否則,我們需要給每一個(gè)子類聲明一個(gè)std::vector變量。

接著往下說(shuō),我們考慮下面的例子:

std::vector<Animal*> list;
Animal a;
Dog d;
Fish f;
list.push_back(&a);
list.push_back(&d);
list.push_back(&f);

for (auto e : list)
{
    e->print();
}

我們知道,這三個(gè)類,都有自己定義的print函數(shù),那么這個(gè)for循環(huán)執(zhí)行的時(shí)候,到底怎么打印呢?

我是 Animal
我是 Animal
我是 Animal

這種結(jié)果是出乎意料,還是不出所料呢,不同的人有不同的見(jiàn)解。

這里應(yīng)該是不出所料的,因?yàn)?,c++是一個(gè)靜態(tài)類型的語(yǔ)言,大部分特性都是靜態(tài)的,所謂靜態(tài),就是編譯的時(shí)候就能確定一些事情,比如說(shuō),調(diào)用哪個(gè)函數(shù)。

由于e的類型是Animal*, 所以在編譯的時(shí)候,就已經(jīng)確定好了,for循環(huán)里的printAnimal::print。這就是所謂靜態(tài)。

我們發(fā)現(xiàn),這個(gè)std::vector確實(shí)能存儲(chǔ)Animal對(duì)象指針Dog對(duì)象指針、 Fish對(duì)象指針, 但好像一旦存儲(chǔ)進(jìn)去了,就無(wú)法區(qū)分,誰(shuí)是誰(shuí)了。

這怎么行,有一些行為,確實(shí)在子類里覆蓋了,比如說(shuō)print的行為。

如何讓靜態(tài)的c++編譯器生成一些看起來(lái)動(dòng)態(tài)的機(jī)器碼呢,比如說(shuō),上面的循環(huán)里,能夠調(diào)用各自類里面重新定義的print函數(shù),而不簡(jiǎn)單粗暴的直接使用Animal::print呢?

虛函數(shù)登場(chǎng)

虛函數(shù)定義

虛函數(shù)是一種特殊的類成員函數(shù), 這種函數(shù)在編譯器,無(wú)法確定真正的函數(shù)地址在哪里,所以稱之為虛函數(shù)。

程序運(yùn)行的時(shí)候,根據(jù)具體的對(duì)象是什么,就調(diào)用什么相應(yīng)的版本。

用嚴(yán)格一點(diǎn)的話來(lái)說(shuō):調(diào)用該虛函數(shù)出現(xiàn)的那個(gè)類和當(dāng)前對(duì)象的類,這兩個(gè)類之間,最靠下的那個(gè)版本的函數(shù)。

如何讓一個(gè)普通成員函數(shù)成為一個(gè)虛函數(shù)呢,在聲明的時(shí)候,前面加上virtual就行了。

話太繞了,我們來(lái)看例子:

class L1
{
};
class L2: public L1
{
    public:
    virtual void print()
    {
        std::cout << "L2" << std::endl;
    }
};
class L3: public L2
{
}
class L4: public L3
{
    public:
    virtual void print()
    {
        std::cout << "L4" << std::endl;
    }
}
///
void test()
{
    L4 l4;
    L1* pL1 {&l4};
    pL1->print(); // 1. 打印什么
    
    L2* pL2 {&l4};
    pL2->print(); // 2. 打印什么
    
    L3 l3;
    pL2 = &l3;
    pL2->print(); // 3. 打印什么
     
}

我們來(lái)看上面的三個(gè)問(wèn)題.

  • 問(wèn)題1: pL1->print();。這句話其實(shí)很簡(jiǎn)單,壓根就不能編譯,因?yàn)閜L1的類型是L1*, 而L1類里面根本就沒(méi)有print函數(shù)。

  • 問(wèn)題2: pL2->print();。L2*的身子裝了L4指針,這就很明顯了,L2和L4之間,最靠下的print,出現(xiàn)在L4中,所以這里應(yīng)該打印L4

  • 問(wèn)題3: pL2->print();。L2*的身子裝了L3指針,根據(jù)我們的說(shuō)法,也是很明顯的,L2L3之間,最靠下的,還是L2,所以這里應(yīng)該打印L2。

通過(guò)這三個(gè)小問(wèn)題,應(yīng)該稍微了解虛函數(shù)到底調(diào)用哪一個(gè)的問(wèn)題了。

子類中如何改變一個(gè)虛函數(shù)的行為

如果想要在子類中改變一個(gè)虛函數(shù)的行為,那么就必須嚴(yán)格按照基類中該虛函數(shù)的函數(shù)簽名,重新實(shí)現(xiàn)這個(gè)虛函數(shù):

class A
{
    public:
    virtual void print(){}
}
class B: public A
{
    public:
    virtual void print(int a){}
}

來(lái)看看上面的子類B中,我們給print加了一個(gè)參數(shù),此時(shí)B中的print還是A中的那個(gè)print嗎?

答案是否定的,

  • 首先這個(gè)代碼是能編譯過(guò)的
  • 只不過(guò),B::printA::print壓根就沒(méi)啥聯(lián)系,在具體的搜索虛函數(shù)進(jìn)行調(diào)用的時(shí)候,他們被看做完全不同的兩個(gè)函數(shù)。

再來(lái)看看虛函數(shù)的返回值類型所帶來(lái)的問(wèn)題:

class A
{
    public:
    virtual void print(){}
}
class B: public A
{
    public:
    virtual int print(){return 0;}
}

問(wèn),此時(shí)B::print還是A::print嗎?

答案,是的。。。。只不過(guò),這個(gè)直接編譯不過(guò)。

編譯不過(guò)是好的,為什么,因?yàn)樵诰幾g的時(shí)候,就告訴你錯(cuò)在哪了。

上面那個(gè)由于疏忽或者別的原因,給原本的虛函數(shù)多加了一個(gè)參數(shù),這種才可怕呢,因?yàn)榫幾g通過(guò)了。

那怎么防范生成了一個(gè)新的函數(shù)?

override 限定符

如果在子類里面,我們確定要重新實(shí)現(xiàn)一個(gè)虛函數(shù),那么我們就在函數(shù)簽名的后面加上這個(gè)override限定符。

class A
{
    public:
    virtual void print(){};
};
class B: public A
{
    public:
    void print(int a) override {};
}

看上面代碼,B這個(gè)子類中print函數(shù)前面前面,我們?nèi)サ袅?code>virtual, 而在花括號(hào)前面加了override。

此時(shí),編譯器就報(bào)錯(cuò)了,邏輯是這樣的:

  • 編譯器看到override,它就認(rèn)為print是從基類繼承而來(lái)的一個(gè)虛函數(shù),所以它去看看A::print, 發(fā)現(xiàn)這個(gè)函數(shù)沒(méi)有參數(shù)。
  • 回過(guò)頭來(lái),發(fā)現(xiàn)B::print(int) 帶了一個(gè)參數(shù),編譯器直接報(bào)錯(cuò)。

這就讓錯(cuò)誤盡早出現(xiàn)在編譯時(shí)期,棒!

final 限定符

可能會(huì)有這么一種情況,有一個(gè)類A,里面有一個(gè)虛函數(shù)print,你寫(xiě)了一個(gè)類B,繼承了類A,然后override了這個(gè)print函數(shù)。然后別人寫(xiě)了一個(gè)類C繼承了類B,你不想類C擁有override這個(gè)print函數(shù)的權(quán)限。

此時(shí),在類B中,override print 函數(shù)的地方,可以加一個(gè)final:

class A
{
    public:
    virtual void print(){};
}
class B: public A
{
    public:
    void print() override final {}; // 注意看,加了final
}
class C: public B
{
    public:
    void print() override {}; // 編譯報(bào)錯(cuò)
}

上面的代碼演示了,class C中無(wú)法繼續(xù)override print的寫(xiě)法。

還有一種極端的情況,你寫(xiě)了一個(gè)類A,你壓根就不想別人去繼承這個(gè)類A

class A final
{
};
class B: public A // oh, 直接報(bào)錯(cuò)
{
};

加了final之后,就可以阻止別的類來(lái)繼承了。

covariant 返回類型

上面講過(guò),一個(gè)虛函數(shù),想要在子類里override,那么函數(shù)簽名必須一模一樣,包括返回值類型。但是有一種特殊的情況,需要考慮??聪旅娴睦?/p>

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

class L1
{
    public:
    virtual A* get()
    {
        return new A{};
    }
};
class L2: public L1
{
    public:
    B* get() override
    {
        return new B{};
    }
};

我們先注意到,B和A就是兩個(gè)普通的有繼承關(guān)系的類,里面并沒(méi)有出現(xiàn)virtual函數(shù)。

真正要研究的是L2和L1,get 函數(shù)是一個(gè)virtual函數(shù),但是L2里get返回值類型是B*。

這似乎違反了virtual函數(shù)的規(guī)定,那就是函數(shù)簽名必須一致。

但是又能說(shuō)的通:【子類就是父類】。

所以上面的代碼能編譯過(guò)嗎?

答案是能。這種特殊的情況被稱之為covariant 返回類型,有的地方翻譯成協(xié)變返回類型。

接著看如下的代碼:

void test()
{
    L2 l2;
    l2.get()->print(); // 問(wèn)題1,這里打印什么?
    
    L1& rl1{l2};
    rl1.get()->print(); // 問(wèn)題2,這里打印什么?
}
  • 問(wèn)題1:這個(gè)地方不難,就是打印This is B。
  • 問(wèn)題2:我們來(lái)慢慢分析,rl1 聲明的類型是 L1& ,但是引用了一個(gè)子類對(duì)象l2。此時(shí) rl2.get()是遵循虛函數(shù)的調(diào)用邏輯,也就是肯定調(diào)用的是L2::get。L2::get的返回類型是什么,是B*,所以直接得出結(jié)果應(yīng)該是 B::print, 打印This is B

不好意思,問(wèn)題2的結(jié)論是錯(cuò)的。

虛函數(shù)不會(huì)改變?cè)镜暮瘮?shù)返回類型,在L1這個(gè)基類中,返回類型就是A*,即使調(diào)用了L2::get,仍然返回了A*這個(gè)類型,如果你有IDE,你可以將鼠標(biāo)懸停在

rl1.get()->print();

get這個(gè)地方,會(huì)顯示出,返回類型是A*, 于是乎,最后的print其實(shí)是A::print, 所以打印了

This is A。

virtual destructor 虛析構(gòu)函數(shù)

在大部分時(shí)候,我們都無(wú)需為自定的class提供一個(gè)析構(gòu)函數(shù), 因?yàn)榇蟛糠謺r(shí)候自定義的class里面不包含需要釋放的資源,比如說(shuō)內(nèi)存,文件等等。此時(shí)c++會(huì)提供一個(gè)默認(rèn)的析構(gòu)函數(shù)。

但是,如果我們的class里有這種動(dòng)態(tài)的資源,那么就不得不提供一個(gè)自定義的析構(gòu)函數(shù),來(lái)針對(duì)這些動(dòng)態(tài)資源進(jìn)行釋放。

更進(jìn)一步的是,如果一個(gè)擁有動(dòng)態(tài)資源的class同時(shí)繼承了別的class,此時(shí)最好小心一點(diǎn):

這是啥意思, 來(lái)看例子:

class L1
{
    public:
    ~L1()
    {
        std::cout << "L1 正在析構(gòu)" << std::endl;
    }
};
class L2: public L1
{
    int* resource;
    public:
    L2():resource{new int}
    {
    }
    ~L2()
    {
        delete resource;
    }
};
void test()
{
    L2* l2{new L2};
    L1* pl1{l2};
    
    delete pl1;
}

分析以上代碼,pl1 指向了一個(gè)子類L2的對(duì)象,在delete pl1的時(shí)候,編譯器發(fā)現(xiàn),L1 的析構(gòu)函數(shù)是正常函數(shù),所以編譯器在這里指定決定調(diào)用L1::~L1這個(gè)函數(shù),然后就結(jié)束了。

我們會(huì)發(fā)現(xiàn),L2 的析構(gòu)函數(shù)并沒(méi)有被調(diào)用到,也就是說(shuō), resource 所指向的資源沒(méi)有被回收?。。?/p>

怎么辦呢,將 L1 中的析構(gòu)函數(shù)標(biāo)記成virtual:

class L1
{
    public:
    virtual ~L1(){};
}

這樣才能保證,任何繼承自L1的類中的動(dòng)態(tài)資源被回收。

結(jié)論:如果寫(xiě)了一個(gè)類,這個(gè)類有可能被別的類繼承的話,那么最好將這個(gè)類的析構(gòu)函數(shù)標(biāo)記成virtual的:

class A
{
    public:
    virtual ~A() = default;
}

關(guān)于這一點(diǎn),有很多大師級(jí)人物都討論過(guò),不同的人有不同的看法,不過(guò),上面的結(jié)論還是穩(wěn)妥的,雖然有一點(diǎn)性能消耗。

虛函數(shù)如何實(shí)現(xiàn)的

為什么要有這個(gè)疑問(wèn),難道這種實(shí)現(xiàn)不正常嗎?

不正常,非常不正常,C++是一個(gè)靜態(tài)語(yǔ)言,必須先編譯再運(yùn)行,執(zhí)行什么函數(shù),一定是編譯時(shí)就決定好的。

而虛函數(shù)打破了這種既有的規(guī)則,而這種規(guī)則的打破依賴于函數(shù)指針。

下面來(lái)講講虛函數(shù)這一套邏輯到底是怎么跑起來(lái)的。

函數(shù)指針

這是一種指針,這個(gè)指針指向的是一塊代碼,用這個(gè)指針可以進(jìn)行函數(shù)調(diào)用:

void print_v1()
{
    std::cout << "print_v1" <<std::endl;
}

void print_v2()
{
    std::cout << "print_v2" <<std::endl;
}

void test()
{
    auto f {print_v1};
    f(); // 打印 print_v1
    f = print_v2;
    f(); // 打印 print_v2
}

觀察上面的代碼,發(fā)現(xiàn),兩個(gè)f()調(diào)用了不同的函數(shù),這是一種動(dòng)態(tài)行為。也就是說(shuō),程序運(yùn)行的時(shí)候,根據(jù)f本身的指向,才能決定真正調(diào)用哪一塊代碼。

虛函數(shù)表

有了函數(shù)指針,使得動(dòng)態(tài)行為有了可能,剩下的就是奇思妙想,讓虛函數(shù)邏輯跑起來(lái)。

大部分編譯器采用了所謂虛函數(shù)表的東西來(lái)實(shí)現(xiàn)虛函數(shù)邏輯。

這種東西文字描述不清,直接看例子:

class L1
{
    public:
    virtual void func1()
    {
    }
    virtual void func2()
    {
    }
};
class Sub1: public L1
{
    public:
    void func1() override
    {
    }
};
class Sub2: public L1
{
    public:
    void func2() override
    {}
};

先描述一下,上面有三個(gè)class,L1是一個(gè)基類,里面有兩個(gè)virtual 函數(shù):

  • func1
  • func2

然后

  • Sub1繼承了L1, 然后override了 func1
  • Sub2繼承了L1, 然后override了 func2

此時(shí),先來(lái)考慮一個(gè)小問(wèn)題,sizeof 三個(gè) class,應(yīng)該是多大呢,假如是64bit機(jī)器。

答案是都是占8字節(jié),也就是64bit。

那么這8字節(jié)存了啥東西?

答案就是,這8字節(jié)其實(shí)是一個(gè)指針,指向哪,先不說(shuō),一會(huì)再來(lái)說(shuō)明。

虛函數(shù)表的概念

對(duì)于上面的例子來(lái)說(shuō),編譯器生成了三個(gè)虛函數(shù)表,也就是L1、Sub1、Sub2每個(gè)class,各一個(gè)。

注意這個(gè)虛函數(shù)表是每個(gè)class一個(gè),而不是每個(gè)對(duì)象一個(gè),一定要搞明白。

這很類似于 class 里的靜態(tài)成員,這么說(shuō)就好理解了。

那虛函數(shù)表長(zhǎng)啥樣?

其實(shí)虛函數(shù)表就是一個(gè)數(shù)組,數(shù)組里的每一項(xiàng)就是一個(gè)簡(jiǎn)單的函數(shù)指針。

我們來(lái)畫(huà)一畫(huà)上面例子的虛函數(shù)表:

在右邊的代碼段里,我們可以看見(jiàn),一共有四個(gè)不同的函數(shù),這與我們的代碼是一致的。

再來(lái)看左邊的虛函數(shù)表,可以清晰的看出來(lái),每個(gè)類里的兩個(gè)虛函數(shù)都真實(shí)地指向了正確的版本。

光有這個(gè)虛函數(shù)表,是沒(méi)用的,在調(diào)用虛函數(shù)的地方,必須與這個(gè)虛函數(shù)表聯(lián)系起來(lái)。

還記得剛才說(shuō)的那個(gè)8字節(jié)的指針嗎。

那個(gè)指針就是起到這種關(guān)聯(lián)的。

我們看下面的例子:

void test()
{
    L2 l2;
    L1* p{&l2};
    p->func1();
}

我們畫(huà)出上面的整個(gè)關(guān)系圖:

此時(shí)用 p->func1() 的時(shí)候,為什么會(huì)調(diào)用到Sub1::func1就一目了然了,一直跟著指針往下走就明白了!

vtable指針

我們將上面的那個(gè)8字節(jié)指針?lè)Q做vtable指針,它的作用就是來(lái)指向相應(yīng)的class的虛函數(shù)表的。

一般而言,這個(gè)變量是在基類里聲明的,子類是繼承了這個(gè)變量。

在對(duì)象初始化的時(shí)候,這個(gè)指針會(huì)指向真正的本class的虛函數(shù)表。

比如說(shuō)

  • Sub1對(duì)象里的vtable就會(huì)指向Sub1的虛函數(shù)表
  • Sub2對(duì)象里的vtable就會(huì)指向Sub2的虛函數(shù)表

虛函數(shù)的消耗

我們從上面的實(shí)現(xiàn)可以看出,在使用虛函數(shù)的class里,強(qiáng)行塞入了一個(gè)vtable指針,占了8字節(jié),這無(wú)疑會(huì)增加內(nèi)存的消耗。

其次,調(diào)用虛函數(shù)的時(shí)候,需要三步走。

  • 從vtable找到虛函數(shù)表
  • 從虛函數(shù)表找到真正的函數(shù)指針
  • 然后由函數(shù)指針找到函數(shù),進(jìn)行調(diào)用

而一般的函數(shù)只有最后一步,這無(wú)疑也是增加了一些步驟的,不過(guò)這種消耗不怎么明顯,所以該用虛函數(shù),還是盡量用吧,不要有什么心理負(fù)擔(dān),然后搞什么靜多態(tài)。

對(duì)了,我們把整個(gè)虛函數(shù)所進(jìn)行的行為稱之為多態(tài),這是一種動(dòng)態(tài)多態(tài),因?yàn)檫@是運(yùn)行時(shí)的行為。

至于什么叫靜多態(tài),那就不屬于本文所討論的了。

總結(jié)

到此這篇關(guān)于C++虛函數(shù)的文章就介紹到這了,更多相關(guān)掌握C++虛函數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C語(yǔ)言中的內(nèi)存管理詳情

    C語(yǔ)言中的內(nèi)存管理詳情

    這篇文章主要介紹了C語(yǔ)言中的內(nèi)存管理詳情,手工申請(qǐng)內(nèi)存使用malloc展開(kāi)全文內(nèi)容,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-05-05
  • C語(yǔ)言三子棋小游戲的實(shí)現(xiàn)代碼

    C語(yǔ)言三子棋小游戲的實(shí)現(xiàn)代碼

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言三子棋小游戲的實(shí)現(xiàn)代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-11-11
  • C語(yǔ)言中字符串處理函數(shù)sscanf的用法

    C語(yǔ)言中字符串處理函數(shù)sscanf的用法

    一直對(duì)于一些日期字符串中數(shù)字的提取比較頭疼,現(xiàn)看到 sscanf 對(duì)于字符串中的內(nèi)容提取較方便,本文主要介紹了C語(yǔ)言中字符串處理函數(shù)sscanf的用法,具有一定參考價(jià)值,感興趣的可以了解一下
    2023-08-08
  • C++ find函數(shù)及用法小結(jié)

    C++ find函數(shù)及用法小結(jié)

    string類的find()函數(shù)用于在字符串中查找字符或子串,返回第一個(gè)匹配的位置,C++中的find()函數(shù)有多種用法,本文通過(guò)實(shí)例代碼給大家詳細(xì)講解,感興趣的朋友一起看看吧
    2023-12-12
  • C語(yǔ)言實(shí)現(xiàn)紙牌計(jì)算24點(diǎn)小游戲

    C語(yǔ)言實(shí)現(xiàn)紙牌計(jì)算24點(diǎn)小游戲

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)紙牌計(jì)算24點(diǎn)小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-10-10
  • c++ 入門(mén)——淺析構(gòu)造函數(shù)和析構(gòu)函數(shù)

    c++ 入門(mén)——淺析構(gòu)造函數(shù)和析構(gòu)函數(shù)

    這篇文章主要介紹了c++ 淺析構(gòu)造函數(shù)和析構(gòu)函數(shù)的相關(guān)資料,幫助大家入門(mén)c++ 編程,感興趣的朋友可以了解下
    2020-08-08
  • C++ 初始化列表詳解及實(shí)例代碼

    C++ 初始化列表詳解及實(shí)例代碼

    這篇文章主要介紹了C++ 初始化列表詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下
    2016-12-12
  • C語(yǔ)言超詳細(xì)講解指針的概念與使用

    C語(yǔ)言超詳細(xì)講解指針的概念與使用

    本文主要講解C語(yǔ)言中指針和字符串的關(guān)系以及指針和數(shù)組的關(guān)系,在看本文之前大家可以先看看博主之前的C語(yǔ)言基礎(chǔ)篇,先對(duì)C語(yǔ)言指針先有個(gè)基礎(chǔ)的了解,有助于對(duì)本文章有更深一步的了解
    2022-05-05
  • Qt增加版本公司等信息兩種方式

    Qt增加版本公司等信息兩種方式

    在項(xiàng)目中生成exe或者動(dòng)態(tài)庫(kù)過(guò)程中可能需要加入公司信息、版本號(hào)、說(shuō)明等等,下面這篇文章主要給大家介紹了關(guān)于Qt增加版本公司等信息的兩種方式,需要的朋友可以參考下
    2024-01-01
  • C語(yǔ)言文件操作大全

    C語(yǔ)言文件操作大全

    這篇文章主要介紹了C語(yǔ)言文件操作大全的相關(guān)資料,需要的朋友可以參考下
    2018-03-03

最新評(píng)論