C++面向?qū)ο笾惡蛯ο竽切┠悴恢赖募毠?jié)原理詳解
一、類和對象、this指針

OOP語言的四大特征是什么?
- 抽象
- 封裝、隱藏
- 繼承
- 多態(tài)
類體內(nèi)實現(xiàn)的方法會自動處理為inline函數(shù)。
類對象的內(nèi)存大小之和成員變量有關(guān)
類在內(nèi)存上需要對齊,是為了減輕cup在內(nèi)存上的io次數(shù)
查看類對象的大小的指令:cl className.cpp /d1reportSingleClassLayout類名
一個類可以定義無數(shù)個對象,每個對象都有自己的成員變量,但是他們共享一套成員方法。
有一個問題:Q1:類中的成員方法是怎么知道要處理哪個對象的信息的?
A1:在調(diào)用成員方法的時候會在參數(shù)列表里隱式的給定對象內(nèi)存的地址。如下所示:

類的成員方法一經(jīng)編譯,所有方法參數(shù)都會加一個this指針,接收調(diào)用該方法的對象的地址,即下圖中的CGoods *this

二、掌握構(gòu)造函數(shù)和析構(gòu)函數(shù)
定義一個SeqStack類:
class SeqStack
{
public:
SeqStack(int size = 10) :_top(-1), _size(size) {
_pstack = new int[size];
}
~SeqStack() {
cout << this << "~SeqStack()" << endl;
delete[] _pstack;
_pstack = nullptr;
}
void push(int val) {
if (full()) {
resize();
}
_pstack[++_top] = val;
}
void pop() {
if (empty()) {
return;
}
--_top;
}
int top() {
return _pstack[_top];
}
bool empty() { return _top == -1; }
bool full() { return _top == _size-1; }
private:
int* _pstack;
int _top;
int _size;
void resize() {
int* ptmp = new int[_size * 2];
for (int i = 0; i < _size; i++) {
ptmp[i] = _pstack[i];
}
delete[] _pstack;
_pstack = ptmp;
_size *= 2;
}
};
/**
運行過程
*/
int main() {
SeqStack sq1;
for (int i = 0; i < 15; i++) {
sq1.push(rand() % 100);
}
while (!sq1.empty()) {
cout << sq1.top() << " ";
sq1.pop();
}
return 0;
}
三、掌握對象的深拷貝和淺拷貝
.data段的對象是程序啟動的時候構(gòu)造的,程序結(jié)束的時候析構(gòu)的
heap堆上對象是new的時候構(gòu)造的,delete的時候析構(gòu)的
stack棧上的對象是在調(diào)用函數(shù)的時候構(gòu)造的,執(zhí)行完函數(shù)時析構(gòu)的
如果對象占用外部資源,淺拷貝就會出現(xiàn)問題:會導(dǎo)致一個對象指向的內(nèi)存釋放,從而造成另一個對象中的指針成為野指針。所以就要對這樣的對象進行深拷貝,在新的對象中重新開辟一塊空間,使兩者互不干涉。
注意:在面向?qū)ο笾?,要避免使用memcpy進行拷貝,因為對象的內(nèi)存占用不確定,會因為對象中保存指針而造成淺拷貝。需要拷貝的時候只能用for循環(huán)逐一拷貝。
深拷貝:
SeqStack& operator=(const SeqStack& src) {
cout << "operator=" << endl;
//防止自賦值
if (this == &src) {
return *this;
}
delete[] _pstack;//需要釋放掉自身占用的外部資源
_pstack = new int[src._size];
for (int i = 0; i <= src._top; i++) {
_pstack[i] = src._pstack[i];
}
_top = src._top;
_size = src._size;
return *this;
}
SeqStack(const SeqStack& src) {
cout << this << "SeqStack(const SeqStack& src)" << endl;
_pstack = new int[src._size];
for (int i = 0; i <= src._top; i++) {
_pstack[i] = src._pstack[i];
}
_top = src._top;
_size = src._size;
}
四、類和對象應(yīng)用實踐
類Queue:
#pragma once
class CirQueue
{
public:
CirQueue(int size = 10) {
_pQue = new int[size];
_front = _rear = 0;
_size = size;
}
CirQueue(const CirQueue& src) {
_size = src._size;
_front = src._front;
_rear = src._rear;
_pQue = new int[_size];
for (int i = _front; i != _rear; i = (i + 1) % _size) {
_pQue[i] = src._pQue[i];
}
}
~CirQueue() {
delete[] _pQue;
_pQue = nullptr;
}
CirQueue& operator=(const CirQueue& src) {
if (this == &src) {
return *this;
}
delete[] _pQue;//需要釋放掉自身占用的外部資源
_size = src._size;
_front = src._front;
_rear = src._rear;
_pQue = new int[_size];
for (int i = _front; i != _rear; i = (i + 1) % _size) {
_pQue[i++] = src._pQue[i];
}
return *this;
}
void push(int val) {
if (full()) {
resize();
}
_pQue[_rear] = val;
_rear = (_rear + 1) % _size;
}
void pop() {
if (empty()) {
return;
}
_front = (_front + 1) % _size;
}
int front() {
return _pQue[_front];
}
bool full() {
return (_rear + 1) % _size == _front;
}
bool empty () {
return _front == _rear;
}
private:
int* _pQue;
int _front;
int _rear;
int _size;
void resize() {
int* ptmp = new int[_size * 2];
int index = 0;
for (int i = _front; i != _rear; i=(i+1)%_size) {
ptmp[index++] = _pQue[i];
}
delete[] _pQue;
_pQue = ptmp;
_front = 0;
_rear = index;
_size *= 2;
}
};類String:
#pragma once
#include <algorithm>
class String
{
public:
String(const char* str = nullptr) {
if (str != nullptr) {
_pChar = new char[strlen(str) + 1];
strcpy(_pChar, str);
}
else {
_pChar = new char[1];
*_pChar = '\0';
}
}
String(const String& str) {
_pChar = new char[strlen(str._pChar)+1];
strcpy(_pChar, str._pChar);
}
~String() {
delete[] _pChar;
_pChar = nullptr;
}
String& operator=(const String& str) {
if (this == &str) {
return *this;
}
delete[] _pChar;//需要釋放掉自身占用的外部資源
_pChar = new char[strlen(str._pChar) + 1];
strcpy(_pChar, str._pChar);
return *this;
}
private:
char* _pChar;
};
五、掌握構(gòu)造函數(shù)的初始化列表
初始化列表和寫在構(gòu)造體里有什么區(qū)別:
初始化列表會直接定義并且賦值;放在構(gòu)造體里會先執(zhí)行定義操作,在對定義好的對象賦值。

對象變量是按照定義的順序賦值的,與構(gòu)造函數(shù)中初始化列表的順序無關(guān)。上圖中的ma是0xCCCCCCCC,mb是10,ma未賦值。
六、掌握類的各種成員方法及其區(qū)別
普通成員方法和常成員方法,是可以重載的,常成員方法可以在對象聲明為const的時候調(diào)用。

對象聲明為const的時候,調(diào)用成員方法是通過const對象的指針調(diào)用的,而普通的成員方法默認生成的是普通的指針對象,不能直接賦值。

只要是只讀操作的成員方法,一律實現(xiàn)成const常成員方法
三種成員方法:

七、指向類成員的指針
class Test {
public:
void func() { cout << "call Test::func" << endl; }
static void static_func() { cout << "call Test::static_func" << endl; }
int ma;
static int mb;
};
int Test::mb=0;
int main() {
Test t1;
Test *t2 = new Test();//在堆上生成對象,并用指針指向
//使用指針調(diào)用類成員方法(前面要加類的作用域Test::)
void (Test:: * pfunc)() = &Test::func;
(t1.*pfunc)();
(t2->*pfunc)();
//定義指向static的類成員方法
void(*pfunc1)() = &Test::static_func;
(*pfunc1)();
//使用指針指向類成員變量,前面要加類的作用域Test::
int Test::* p = &Test::ma;
t1.*p = 20;
cout << t1.*p << endl;
t2->*p = 30;
cout << t2->*p << endl;
int* p1 = &Test::mb;
*p1 = 40;
cout << *p1 << endl;
delete t2;
return 0;
}
輸出為:

總結(jié)
到此這篇關(guān)于C++面向?qū)ο笾惡蛯ο竽切┠悴恢赖募毠?jié)原理的文章就介紹到這了,更多相關(guān)C++類和對象的原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
簡單了解設(shè)計模式中的裝飾者模式及C++版代碼實現(xiàn)
這篇文章主要介紹了簡單了解設(shè)計模式中的裝飾者模式及C++版代碼實現(xiàn),ConcreteComponent的引用(指針)也可以達到修飾的功能,需要的朋友可以參考下2016-03-03
VSCode下.json文件的編寫之(1) linux/g++ (2).json中參數(shù)與預(yù)定義變量的意義解釋
這篇文章主要介紹了VSCode下.json文件的編寫之(1) linux/g++ (2).json中參數(shù)與預(yù)定義變量的意義解釋,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-03-03
C++實現(xiàn)LeetCode(125.驗證回文字符串)
這篇文章主要介紹了C++實現(xiàn)LeetCode(驗證回文字符串).本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-07-07
select函數(shù)實現(xiàn)高性能IO多路訪問的關(guān)鍵示例深入解析
這篇文章主要為大家介紹了select函數(shù)實現(xiàn)高性能IO多路訪問的關(guān)鍵示例深入解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-09-09

