前言
C和C++作為學(xué)習(xí)音視頻技術(shù)首要具備的語言基礎(chǔ),所以十分必要學(xué)習(xí)和復(fù)習(xí)一下之前學(xué)習(xí)C++語言基礎(chǔ)。
這里IDE和環(huán)境配置在前面C語言復(fù)習(xí)的文章里已經(jīng)說過了,還是使用CLion這個軟件,話不多說,直接開始學(xué)習(xí)。
正文
C++作為一門用途更廣、功能更齊全的語言,其知識深度很深,所以這里也就復(fù)習(xí)、學(xué)習(xí)一些基本知識點(diǎn),等后續(xù)在實(shí)際項(xiàng)目中有遇到難點(diǎn)再進(jìn)行補(bǔ)充。
hello world
創(chuàng)建完一個C++項(xiàng)目,還是打印hello world,代碼如下:
#include <iostream>
//命名空間,告訴編譯器使用std命名空間
using namespace std;
int main() {
printf("Hello, World! \n");
//這里的cout就是std里面定義的函數(shù)
//<<是操作符重載,后面細(xì)說
cout << "Hello, World!" << endl;
return 0;
}
打印是:
從這個簡單的程序我們可以看出入口函數(shù)和返回值和C語言是一樣的,但是這里有個命名空間的概念,啥是命名空間呢:

說白了就是C++不像Java那樣有包限制,所以對于同名的需要進(jìn)行區(qū)分,這里就是使用命名空間。
還有就是這里的cout函數(shù),cout函數(shù)其實(shí)就是std::cout的簡寫了,在std這個命名空間下的函數(shù),用來標(biāo)準(zhǔn)化輸出到屏幕,和printf一樣,這里的 << 叫做流插入運(yùn)算符,其中endl是換行,要是使用 \n 也可以,這里就沒啥說的了,相當(dāng)于簡化了printf。
關(guān)鍵字
C++關(guān)鍵字有很多,我們不能和學(xué)習(xí)C語言一樣,全都羅列一遍給看一下,這里就看一些常用的,后面有用到其他的再進(jìn)行補(bǔ)充。

不得不說C++是真的復(fù)雜,這僅僅是一部分關(guān)鍵字,就讓人看的頭暈,不過不怕,先慢慢來,后面有補(bǔ)充再補(bǔ)上。
變量作用域
關(guān)于基本數(shù)據(jù)類型這些知識點(diǎn)在之前C中都介紹過,有點(diǎn)類似,就不說了,這里說一下變量作用域。
在函數(shù)或者一個代碼塊內(nèi)部聲明的變量,叫做局部變量。
在函數(shù)參數(shù)的定義中聲明的變量,叫做形式參數(shù)。
在所有函數(shù)外部聲明的變量,叫做全局變量。
定義常量
關(guān)于啥是常量這類的概念就先不說了,這里說一下定義常量的2種常見方式:
使用#define預(yù)處理器。
使用const關(guān)鍵字。
直接看下面代碼:
#include <iostream>
//這個必須得有
#include <string>
using namespace std;
#define NAME "zyh"
const int AGE = 20;
const string COMPANY = "WY";
int main() {
cout << NAME << endl;
cout << "age : " << AGE << endl;
cout << "company : " << COMPANY << endl;
return 0;
}
打印結(jié)果:

注意這里cout輸出時,如果輸出的string類型,則必須要include 才可以正常輸出,否則只能使用char數(shù)組來表示字符串輸出。
字符串
在前面學(xué)習(xí)C時,我們使用字符串都是使用字符數(shù)組來完成,和Java中的String類簡直不能比,太不方便了,所以在C++中引入了string類,但是依舊可以適應(yīng)C風(fēng)格的字符串,直接看一下代碼:
using namespace std;
int main() {
//C風(fēng)d格定義字符串
char greet[6] = "Hello";
char *greet1 = "Hello";
char greet2[] = "Hello";
//C++使用string類型
string greet3 = "Hello";
cout << greet << endl;
cout << greet1 << endl;
cout << greet2 << endl;
cout << greet3 << endl;
return 0;
}
這里不論是C風(fēng)格還是C++的string類型都可以正常使用字符串,當(dāng)然在C中的那些字符串操作函數(shù)比如strcpy、strcat等等都可以正常使用,除此之外,還還有一些string類的函數(shù),這就很像Java的一些方法了,比如append、length方法等。
指針
關(guān)于指針我們在前面文章學(xué)習(xí)C中已經(jīng)了解過了,C++的指針差不多,指針的作用主要就倆點(diǎn):
- 簡化一些任務(wù),使用指針
- 動態(tài)內(nèi)存分配,當(dāng)動態(tài)內(nèi)存分配時,必須要使用指針
指針的定義和使用和C語言的一模一樣,包括定義和取地址符號 & 以及獲取指針指向的值 * 這里就不再說了,不清楚可以回顧一下前面的C語言中的指針。
引用
關(guān)于引用這個概念比較特殊,它類似于指針,但是又不是指針,相當(dāng)于變量名的別名,我們來看一下:

這里這個概念比較特殊,引用不能為空,必須初始化賦值,這個概念感覺有點(diǎn)多余,畢竟C++中有指針的概念,引用在Java中倒是基本概念,以為有引用類型。所以這里還是要區(qū)分一下,尤其是使用 & 來定義,下面代碼簡單看一下:
using namespace std;
int main() {
//聲明變量
int a = 10,b = 20;
//聲明引用變量
int &i = a;
int &j = b;
cout << "a ==" << a << "\t &i ==" << i <<endl;
cout << "b ==" << b << "\t &j ==" << j <<endl;
//改變變量的值,引用也會變化
a = 5,b = 6;
cout << "a ==" << a << "\t &i ==" << i <<endl;
cout << "b ==" << b << "\t &j ==" << j <<endl;
return 0;
}
這里聲明了引用,也就相當(dāng)于a和i指向同一塊內(nèi)存區(qū)域,但是它可以和a一樣使用,也就相當(dāng)于變量,其地址是一樣的,打印如下:

函數(shù)參數(shù)為引用類型
在前面學(xué)習(xí)C語言時,我們說道C的函數(shù)參數(shù)可以是值傳遞也可以是指針傳遞,當(dāng)使用值傳遞時會復(fù)制值,函數(shù)執(zhí)行不影響傳遞進(jìn)來的值,指針由于是地址,函數(shù)執(zhí)行肯定會改變,那這個引用類型呢 又是如何。
所以C++這個引用類型設(shè)計(jì)的感覺有問題,它和指針是一樣的,前面也說了,引用只是變量的一個別名,所以參數(shù)類型是引用,它的效果和是指針是一樣的,看下面代碼:
using namespace std;
void swap(int& x,int& y);
void swap1(int x,int y);
int main() {
int a = 100,b = 200;
cout << "交換前 a = " << a << " b = " << b << endl;
swap(a,b);
//發(fā)生了變化,函數(shù)執(zhí)行影響了傳遞的參數(shù)值
cout << "第一次交換后 a = " << a << " b = " << b << endl;
swap1(a,b);
//沒有發(fā)生變化,值傳遞會復(fù)制參數(shù),不會影響
cout << "第二次交換后 a = " << a << " b = " << b << endl;
return 0;
}
//參數(shù)是引用類型
void swap(int& x,int& y){
int temp;
temp = x;
x = y;
y = temp;
}
//參數(shù)是值
void swap1(int x,int y){
int temp;
temp = x;
x = y;
y = temp;
}
這里說了2種交換,一種是傳值一種是引用,打印如下:

會發(fā)現(xiàn)使用引用也會導(dǎo)致原來值發(fā)生變化。
類和對象
終于到了C++的重頭戲了,也就是面向?qū)ο?,熟悉Java語言對于面向?qū)ο罂隙ㄊ鞘值角軄恚@里還是來看看C++是如何面向?qū)ο蟮摹?/p>
還是直接看代碼:
#ifndef CPLUSTEST_PERSON_H
#define CPLUSTEST_PERSON_H
class Person{
//公共屬性
public:
Person(); //空參數(shù)構(gòu)造函數(shù)
~Person(); //析構(gòu)函數(shù)
Person(char *name,int age); //有參構(gòu)造函數(shù)
//成員變量
char *name;
int age;
//函數(shù)
void setName(char *name);
char *getName();
void setAge(int age);
int getAge();
};
#endif //CPLUSTEST_PERSON_H
這是person.h文件,前面在學(xué)習(xí)C語言種說過,頭文件一般是定義函數(shù)、類啥的,這里也一樣,不過這里僅僅只是定義,和Java還是有著非常大的區(qū)別,真正實(shí)現(xiàn)的地方在下面:
#include <iostream>
#include "person.h"
using namespace std;
Person::Person(char *name, int age) {
this -> name = name;
this -> age = age;
}
Person::~Person() {
cout << "Person銷毀" << endl;
}
Person::Person() {
cout << "執(zhí)行 Person 空構(gòu)造函數(shù)" << endl;
}
void Person::setAge(int age) {
this -> age = age;
}
void Person::setName(char *name) {
this -> name = name;
}
int Person::getAge() {
return this -> age;
}
char *Person::getName() {
return this -> name;
}
這個是person.cpp文件,導(dǎo)入前面頭文件,這里是進(jìn)行實(shí)現(xiàn),這里和Java的類定義區(qū)別很大,在Java或者kotlin中一般定義后就直接都實(shí)現(xiàn)了,不會單獨(dú)搞2個地方,然后就是調(diào)用的地方:
int main() {
//棧里面定義的 在該方法執(zhí)行完就會回收掉Person對象
Person personTemp;
personTemp.setName("張三");
personTemp.setAge(10);
cout << personTemp.getName() << "\t" << personTemp.getAge() << endl;
//如果使用new初始化構(gòu)造函數(shù),
Person *person = new Person("zyh",18);
cout << person -> name << "\t" << person->getAge() << endl;
//釋放person內(nèi)存
delete person;
return 0;
}
同樣這里也有著很大的區(qū)別,首先是對象聲明,在Java中一個對象如果沒有調(diào)用構(gòu)造函數(shù)它就是null,無法調(diào)用其方法,但是在C++中確不一樣,同時使用new操作符新建的對象,需要手動delete釋放內(nèi)存,Java是有GC自動回收,也比較麻煩。
這里先簡單總結(jié)一下,下面再細(xì)說:

C++類成員函數(shù)
啥是類成員函數(shù)就不用說了,學(xué)過面向?qū)ο笳Z言的都知道,這里主要說一下類成員函數(shù)有2種定義的方式。
一種是直接在類中定義,這也是Java語言的方式,還有一種就是在類中聲明,定義在別的地方,這是C++推薦的方式,比如下面類Box在頭文件中聲明:
#ifndef CPLUSTEST_BOX_H
#define CPLUSTEST_BOX_H
//第一種是類成員函數(shù)直接在類中就定義
class Box{
public:
double length; //長度
double width; //寬度
double height; //高度
double getVolume(){
return length * width * height;
}
double getVolume2(); //體積
};
#endif //CPLUSTEST_BOX_H
其中有2個成員函數(shù),getVolume2我們放在.cpp文件中進(jìn)行定義:
#include "box.h"
double Box::getVolume2() {
return length * width * height * 2;
}
上面2種方式都可以。其中重點(diǎn)說明一下在類中定義的成員函數(shù)把函數(shù)聲明為內(nèi)聯(lián)的,啥是內(nèi)聯(lián),后面再說;其中 :: 叫做范圍解析運(yùn)算符。
C++的構(gòu)造和析構(gòu)函數(shù)
關(guān)于構(gòu)造函數(shù)我們自然都很熟悉了,這里只說一點(diǎn)就是使用初始化列表來初始化字段的情況,其實(shí)就是名字意思,使用初始化列表來初始化字段。
這個我們知道在C++中構(gòu)造函數(shù)的參數(shù)是參數(shù),和字段沒關(guān)系,那這時如何在構(gòu)造函數(shù)中初始化字段呢,如果是kotlin的數(shù)據(jù)類是自動幫我做的,如果參數(shù)有val/var修飾符,其他類也就在init代碼塊中進(jìn)行賦值,C++有個不一樣的寫法,就是下面這樣:
#ifndef CPLUSTEST_BOX_H
#define CPLUSTEST_BOX_H
//第一種是類成員函數(shù)直接在類中就定義
class Box{
public:
double length; //長度
double width; //寬度
double height; //高度
double getVolume(){
return length * width * height;
}
double getVolume2(); //體積
//自定義的構(gòu)造函數(shù)
Box(double len);
};
#endif //CPLUSTEST_BOX_H
這里的Box定義加了一個有參構(gòu)造函數(shù),然后在定義的地方:
Box::Box(double len) : length(len) {
std::cout << "構(gòu)造函數(shù)輸入len" << endl;
}
就是這個在函數(shù)后直接 : 對字段進(jìn)行賦值,這種寫法還是蠻新奇的。
還有就是析構(gòu)函數(shù),和無參構(gòu)造函數(shù)一樣,不寫的話編譯器會自己加上。那析構(gòu)函數(shù)是啥呢,就是和默認(rèn)構(gòu)造函數(shù)一樣只不過前面多個~,可以使用析構(gòu)函數(shù)跳出程序、關(guān)閉資源等。
還是前面的Person類,里面有定義析構(gòu)函數(shù),然后我們分別使用2個對象來看一下打?。?/p>
int main() {
//棧里面定義的 在該方法執(zhí)行完就會回收掉Person對象
Person personTemp;
personTemp.setName("張三");
personTemp.setAge(10);
cout << personTemp.getName() << "\t" << personTemp.getAge() << endl;
//如果使用new初始化構(gòu)造函數(shù),
Person *person = new Person("zyh",18);
cout << person -> name << "\t" << person->getAge() << endl;
//釋放person內(nèi)存
delete person;
return 0;
}
打印如下:

會發(fā)現(xiàn)這里不論是自動回收掉的對象還是手動delete的對象都會在對象釋放時調(diào)用析構(gòu)函數(shù)。當(dāng)然使用new關(guān)鍵字創(chuàng)造的對象,在不調(diào)用delete時是不會釋放的。
C++拷貝構(gòu)造函數(shù)
什么是拷貝構(gòu)造函數(shù)呢,這個其實(shí)非常簡單,就是使用一個類之前創(chuàng)建的對象來創(chuàng)建新的對象,比如我有Box A,現(xiàn)在想要一個Box B,讓B和A的內(nèi)容一樣,這時就要考慮了,如果是Java代碼的話B、A2個引用指向同一對象,這不太符合要求,所以會調(diào)用拷貝函數(shù),肯定會創(chuàng)建出一個新對象。
在C++中,直接就有了拷貝構(gòu)造函數(shù)這個概念,讓復(fù)制更方便。但是和Java的復(fù)制一樣,Java復(fù)制需要考慮淺拷貝和深拷貝的問題,在C++中如果類的成員是指針變量,仔細(xì)想一下,直接把A的指針復(fù)制到B的指針變量中,這樣A、B2個對象中該變量都是一個地址,則會相互影響,肯定不行。
所以即使默認(rèn)會有拷貝構(gòu)造函數(shù),當(dāng)類成員變量是指針的時候,也要進(jìn)行重寫拷貝構(gòu)造函數(shù)。
#ifndef CPLUSTEST_LINE_H
#define CPLUSTEST_LINE_H
class Line{
public:
int getLength();
Line(int len);
//拷貝構(gòu)造函數(shù),必須要定義
Line(const Line &obj);
~Line();
private:
//這個是指針變量
int *ptr;
};
#endif //CPLUSTEST_LINE_H
比如上面代碼中的成員變量有指針變量時,
#include "line.h"
#include <iostream>
using namespace std;
Line::Line(int len) {
cout << "調(diào)用構(gòu)造函數(shù)" << endl;
//由于傳遞進(jìn)來的是個int值,所以要先給指針分配內(nèi)存
ptr = new int ;
//指針指向的內(nèi)存值是len
*ptr = len;
}
Line::Line(const Line &obj) {
cout << "調(diào)用拷貝構(gòu)造函數(shù)" << endl;
//這里拷貝的時候就需要深拷貝了,新的對象的指針要重新分配內(nèi)存
ptr = new int ;
//指針指向的值進(jìn)行賦值
*ptr = *obj.ptr;
}
Line::~Line() {
cout << "釋放內(nèi)存" << endl;
delete prt;
}
從上面代碼我可以看出一個問題,就是C++的淺拷貝和深拷貝問題,和Java一樣,如果是這種指針變量,需要重新分配內(nèi)存。
上面例子說明了當(dāng)對象需要另外一個對象進(jìn)行初始化時會調(diào)用拷貝構(gòu)造函數(shù)。
C++友元函數(shù)
這個概念還真沒有在Java中存在過,不過也非常容易理解其含義,友元友元就是好朋友friend的意思,哈哈,也就是友元函數(shù)是聲明在類中,但定義在類外,并且可以通過這個友元函數(shù)訪問類的私有和保護(hù)成員。
這里也就相當(dāng)于給一個類開了一個后門一樣,這個友元函數(shù)不是類的成員函數(shù),但是可以通過它訪問類的私有變量。
還是直接看個代碼例子:
#ifndef CPLUSTEST_BOX_H
#define CPLUSTEST_BOX_H
class Box{
double width; //默認(rèn)是私有變量
public:
friend void printWidth(Box box); //聲明為友元函數(shù)
void setWidth(double width);
};
#endif //CPLUSTEST_BOX_H
這里聲明了一個友元函數(shù),用來獲取私有變量的,
#include <iostream>
#include "box.h"
using namespace std;
void Box::setWidth(double width) {
this->width = width;
}
void printWidth(Box box){
cout << "width = " << box.width << endl;
}
友元函數(shù)因?yàn)椴皇荁ox中的,所以不能使用Box::來調(diào)用,完成定義后,便可以使用了:
using namespace std;
int main() {
Box *box = new Box();
box->setWidth(10);
printWidth(*box);
delete box;
}
感覺有點(diǎn)神奇,這個width屬性沒有任何get方法對外,居然可以通過友元訪問到,不得不說C++的設(shè)計(jì)很到位。
C++內(nèi)聯(lián)函數(shù)
之前看內(nèi)聯(lián)函數(shù)的概念第一次是在kotlin中,由于在Java中沒有這個概念,現(xiàn)在再來看C++,說明kotlin也是不斷借鑒其他語言的優(yōu)勢來完善自己。
內(nèi)聯(lián)內(nèi)聯(lián)就是其字面意思,如果一個函數(shù)定義為內(nèi)聯(lián)時,在編譯時,編譯器會把該函數(shù)的代碼副本放置到每個調(diào)用該函數(shù)的地方,其實(shí)就是為了效率。
對于一般函數(shù)都是在運(yùn)行時才被替代加入棧中,但是對于內(nèi)聯(lián)函數(shù)在編譯時便進(jìn)行復(fù)制,這也就是用空間代價換時間的一種方式,所以內(nèi)聯(lián)函數(shù)一般不超過10行。
C++繼承
面向?qū)ο髞碚f,繼承這個就不用考慮了,熟悉Java的都很了解,這里就不說了。
區(qū)別就是C++可以多繼承,就是可以繼承多個父類,這個在Java中只允許繼承一個父類,實(shí)現(xiàn)多個接口。
動態(tài)內(nèi)存
其實(shí)這個也非常簡單,和Java的內(nèi)存分配類似,在C++中函數(shù)執(zhí)行也是出入棧,所以函數(shù)中定義變量將占用棧內(nèi)存,在函數(shù)執(zhí)行完會釋放。
當(dāng)使用new關(guān)鍵字可以動態(tài)分配內(nèi)存,這個內(nèi)存是在堆中的,使用完需要使用delete來進(jìn)行釋放。
這里的new對象就沒啥說的了,其中對于指針變量,可以new內(nèi)置類型的指針,這個在前面說拷貝函數(shù)時已經(jīng)說過如何使用了。
命名空間
這個其實(shí)沒啥說的,就是把一堆變量和函數(shù)給劃分到一塊,這一塊給命個名子即可。
總結(jié)
其實(shí)C++部分就是在C上面加了面向?qū)ο蟮母拍睿嫦驅(qū)τ谑煜ava的理解起來也非常容易,所以本篇文章就簡單復(fù)習(xí)一下,等后面實(shí)際問題時再進(jìn)行補(bǔ)充。
本文轉(zhuǎn)自 https://juejin.cn/post/7021434763244208165,如有侵權(quán),請聯(lián)系刪除。