C++ 一把窺探OC底層的利刃

GitHub Repo:coderZsq.github.io
Follow: coderZsq · GitHub
Resume: coderzsq.github.io/coderZsq.we…webpack

平常扯淡

做爲iOS開發的菜雞, 平日裏的工做就是作業務, 調UI, 對於咱們這種弱雞玩家來講, 編程呢, 其實就是調方法, 調屬性, 調庫...ios

但光是作業務UI的工做確定會讓本身日漸乏味, 爲了避免重複寫那些看了想吐的代碼, 去年就花了點時間寫了一個代碼生成工具, 用於配置一鍵生成垃圾代碼的, 這樣對於菜雞開發者也就是我來講, 只須要調調本身封裝的一些UI庫, 搭搭積木就完成工做了, 其餘時間就能夠自由支配玩點有趣的事情.git

去年渾渾噩噩, 學了一堆有的沒的, 什麼Vue, React, Spring的調用, 但一直這樣無所事事的我, 今年也想深刻的學習一些什更深層次的東西, 而不只僅只是在UI層的淺嘗輒止.github

然而在iOS這個領域中, 想要深刻研究, C\C++, 彙編, Linux, 像三座大山同樣攔在我面前, 因此作爲菜雞的我路漫漫而其修遠兮.web

本文就經過學習C++的語法開始, 一點一點的剖析OC的本質, 直到世界的盡頭.編程

C++ 做者的建議

  1. 在C++中幾乎不須要用宏, 用const和enum定義顯式的常量, 用inline避免函數調用的額外開銷, 用模板去刻畫一組函數或類型, 用namespace去避免命名衝突.
  2. 不要在你須要變量以前去聲明, 以保證你能當即對它進行初始化.
  3. 不要用malloc, new運算會作的更好.
  4. 避免使用void*, 指針算數, 聯合和強制, 大多數狀況下, 強制類型轉換是設計錯誤的指示器.
  5. 儘可能少用數組和C風格的字符串, 標準庫中的string和vector能夠簡化程序.
  6. 更加劇要的是, 試着將程序考慮爲一組由類和對象表示的互相做用的概念年, 而不是一堆數據結構和一些能夠撥弄的二進制.

標準輸入輸出

咱們先來看C++的標準輸入輸出:數組

char buf[10];
scanf("%s", buf);
printf("%s\n", buf);
複製代碼

這個是你們都很熟悉的C的輸入輸出, 也就是scanfprintf. 但C語言是越界不檢的, 因此這裏的char buf[10]的緩衝區可能會形成寫越界, 致使不安全訪問.安全

char buf[10];
fgets(buf, 10, stdin);
printf("%s\n", buf);
複製代碼

因此C使用了fget強制的截斷了輸入來保證, 訪問的安全.bash

12345678901234567
123456789
Program ended with exit code: 0
複製代碼

以上是C語言安全標準輸入的打印日誌, 咱們能夠看到, 因爲設置了10爲輸入截斷參數, 後面就再也不輸入了.數據結構

char buf[10];
cin>>buf;
cout<<buf<<endl;
複製代碼

咱們再來看看C++的輸入流cincout, 能夠看到的是, cin 操做char buf[10] 一樣不安全, 也會形成寫越界.

char buf[10];
cin.getline(buf, 10);
cout<<buf<<endl;
複製代碼

咱們能夠看到, cin.getline很好的解決了這個問題, 但其實做用和fgets並沒有二異.

string buf;
cin>>buf;
cout<<buf<<endl;
複製代碼

然而使用string代替char buf[10] 避免char[]安全問題, 纔是C++的正確打開方式, 這下不管怎樣亂搞, 都不會有問題.

int data = 1234;
cout<<hex<<data<<endl;
cout<<oct<<data<<endl;
cout<<dec<<data<<endl;
複製代碼

接下來, 咱們來看看, C++的進制轉換, 使用<<hex,表明16進制, <<oct, 表明8進制, <<dec,表明10進制.

4d2
2322
1234
Program ended with exit code: 0
複製代碼

使用<<hex切換進制, 注意雖然默認是10進制但切換其餘進制後, 默認爲切換後的進制.

int data = 1234;
cout<<data<<endl;
cout<<setw(10)<<setiosflags(ios::left)<<data<<endl;
cout<<setw(10)<<setiosflags(ios::right)<<data<<endl;
複製代碼

使用setwsetiosflags來設置域寬和左右對齊.

#include <iomanip>
複製代碼

在使用setwsetiosflags以前須要添加頭文件.

1234
1234      
      1234
Program ended with exit code: 0
複製代碼

以上就是使用setwsetiosflags的打印結果.

int a = 12;
int b = 3;
int c = 5;    
cout<<setfill('0')<<setw(2)<<a<<":"<<setw(2)<<b<<":"<<setw(2)<<c<<endl;
複製代碼

使用setfill進行填充, 這個就很少說了, 試試就知道.

float f = 1.23456;
cout<<f<<endl;
cout<<setprecision(4)<<f<<endl;
cout<<setprecision(4)<<setiosflags(ios::fixed)<<f<<endl;
複製代碼

使用setprecision來調整浮點數的精度.

函數重載

函數重載, 會出現重名的函數, 重名的函數會根據語境來決定調用, 運算符重載也是一種函數重載.

void func(int a) {
    cout<<"void func(int a)"<<endl;
}
void func(float a) {
    cout<<"void func(float a)"<<endl;
}
void func(char a) {
    cout<<"void func(char a)"<<endl;
}

int main(int argc, const char * argv[]) {
    
    int a = 1;
    func(a);
    float b = 1.2;
    func(b);
    char c = 'c';
    func(c);

    return 0;
}
複製代碼
void func(int a)
void func(float a)
void func(char a)
複製代碼

函數重載是一種簡潔的須要, 函數返回值類型不能構成函數重載的標誌.

重載底層實現使用命名傾軋來改變函數名, 區分不一樣參數不一樣的同名函數.

#ifndef mystr_h
#define mystr_h

#include <stdio.h>

extern "C" int myStrlen(const char *s);

#endif /* mystr_h */

#include "mystr.h"
//extern "C" {
int myStrlen(const char *s) {
    int len = 0;
    while (*s) {
        len++;
        s++;
    }
    return len;
}
//}
複製代碼

C++默認進行傾軋, 使用extern "C" 來避免傾軋形成的連接錯誤. 用來鏈接C的庫.

運算符重載

和上面講的相同, 運算符重載的本質其實也是一種函數重載, 相信熟悉Swift的你, 必定不會陌生.

typedef struct _pos {
    int x_;
    int y_;
} Pos;

bool operator== (Pos one, Pos another) {
    if (one.x_ == another.x_ && one.y_ == another.y_) {
        return true;
    } else {
        return false;
    }
}

int main(int argc, const char * argv[]) {
    
    Pos ps = {1, 2};
    Pos fdPs = {3, 4};
    if (ps == fdPs) {
        cout<<"=="<<endl;
    } else {
        cout<<"!="<<endl;
    }
    
    return 0;
}
複製代碼
Pos ps = {1, 2};
Pos fdPs = {3, 4};
if (operator==(ps, fdPs)) {
    cout<<"=="<<endl;
} else {
    cout<<"!="<<endl;
}
複製代碼

運算符重載其實也就是函數調用.

默認參數

OC沒有默認參數, 而C++卻有...., Swift也有這個特性.

void foo(int a = 1, int b = 2, int c = 3) {
    cout<<"====="<<endl;
    cout<<"a = "<<a<<endl;
    cout<<"b = "<<b<<endl;
    cout<<"c = "<<c<<endl;
}

int main(int argc, const char * argv[]) {
    
    foo();
    foo(2);
    foo(2, 3);
    foo(2, 3, 4);
    return 0;
}
複製代碼
=====
a = 1
b = 2
c = 3
=====
a = 2
b = 2
c = 3
=====
a = 2
b = 3
c = 3
=====
a = 2
b = 3
c = 4
複製代碼

默認參數和函數重載可能會產生衝突. 優先選擇默認參數的方案.

引用

引用的本質是對指針的包裝, 避免使用裸露的指針, 引用是一種聲明關係, 不開闢空間, 這裏說不開闢, 只是代碼層面看, 實際開始會開闢空間的.

int a = 100;
int &ra = a;
cout<<"a = "<<a<<endl;
cout<<"ra = "<<ra<<endl;
cout<<"&a = "<<&a<<endl;
cout<<"&ra = "<<&ra<<endl;
複製代碼
a = 100
ra = 100
&a = 0x7ffeefbff5cc
&ra = 0x7ffeefbff5cc
Program ended with exit code: 0
複製代碼
void swap(int &ra, int &rb) {
    ra ^= rb;
    rb ^= ra;
    ra ^= rb;
}

int main(int argc, const char * argv[]) {
    
    int a = 10;
    int b = 20;
    swap(a, b);
    cout<<a<<"-"<<b<<endl;
    
    return 0;
}
複製代碼
20-10
複製代碼

傳引用等於傳做用域, 這一點熟悉OC的同窗其實根本不用在乎.

void swap(char **a, char **b) {
    char *t = *a;
    *a = *b;
    *b = t;
}

void swap(char * &ra, char * &rb) {
    char *t = ra;
    ra = rb;
    rb = t;
}

int main(int argc, const char * argv[]) {
    
    char *p = "china";
    char *q = "canada";
    cout<<"p = "<<p<<endl;
    cout<<"q = "<<q<<endl;
    swap(&p, &q);
    cout<<"p = "<<p<<endl;
    cout<<"q = "<<q<<endl;
    swap(p, q);
    cout<<"p = "<<p<<endl;
    cout<<"q = "<<q<<endl;

    return 0;
}
複製代碼
p = china
q = canada
p = canada
q = china
p = china
q = canada
複製代碼

上面是指針的引用, 並無引用的指針.

int * p;
int ** pp = &p;
int *** ppp = &pp;
int **** pppp = &ppp;
    
int a;
int &ra = a;
int &rb = ra;
int &rc = rb;
複製代碼

引用爲平級, 沒有指針的指針這種概念.

int arr[10] = {1, 2, 3 ,4, 5, 6, 7};
int * const & parr = arr;
for (int i = 0; i < sizeof(arr) / sizeof(int); i++) {
    cout<<parr[i]<<endl;
}
    
int (& rarr)[10] = arr;
cout<<"sizeof = "<<sizeof(rarr)<<endl;
    return 0;

複製代碼

上面是數組的引用, 數組的引用呢, 在OC上就是*, 而底層原來是這樣實現的.

int foo() {
    int a = 200;
    return a;
}

int main(int argc, const char * argv[]) {
    
    const int & c = 100;
    cout<<c<<endl;

    int a = 3; int b = 5;
    const int & ret = a + b;
    cout<<ret<<endl;
    
    const int & ra = foo();
    cout<<ra<<endl;
    
    double d = 100.12;
    double & rd = d;
    const int & rd2 = d;
    cout<<rd<<endl;
    cout<<rd2<<endl;

    rd = 200.14;
    cout<<rd<<endl;
    cout<<rd2<<endl;
    
    return 0;
}
複製代碼
100
8
200
100.12
100
200.14
100
Program ended with exit code: 0
複製代碼

常引用, 引用的是一個寄存器常量. 和宏在預編譯期間替換不一樣, 常引用在彙編期間經過寄存器替換.

void foo(int & ri, char & rc) {
    cout<<sizeof(ri)<<" "<<sizeof(rc)<<endl;
}

struct TypeC {
    char c;
};

struct TypeP {
    char * pc;
};

struct TypeR {
    char & rc;
};

void mySwap(int * pa, int * pb) {
    int t = *pa;
    *pa = *pb;
    *pb = t;
}

void mySwap(int & ra, int & rb) {
    int t = ra;
    ra = rb;
    rb = t;
}

int main(int argc, const char * argv[]) {
    
    int a; char c;
    foo(a, c);
    
    cout<<"sizeof(TypeC) = "<<sizeof(TypeC)<<endl;
    cout<<"sizeof(TypeP) = "<<sizeof(TypeP)<<endl;
    cout<<"sizeof(TypeR) = "<<sizeof(TypeR)<<endl;

    int n = 3, m = 5;
    mySwap(&n, &m);
    cout<<n<<" "<<m<<endl;
    mySwap(n, m);
    cout<<n<<" "<<m<<endl;

    return 0;
}
複製代碼

咱們等下用匯編來對比一下指針和引用以前的區別.

0x100001060 <+0>:  pushq  %rbp
    0x100001061 <+1>:  movq   %rsp, %rbp
    0x100001064 <+4>:  movq   %rdi, -0x8(%rbp)
    0x100001068 <+8>:  movq   %rsi, -0x10(%rbp)
    0x10000106c <+12>: movq   -0x8(%rbp), %rsi
    0x100001070 <+16>: movl   (%rsi), %eax
    0x100001072 <+18>: movl   %eax, -0x14(%rbp)
    0x100001075 <+21>: movq   -0x10(%rbp), %rsi
    0x100001079 <+25>: movl   (%rsi), %eax
    0x10000107b <+27>: movq   -0x8(%rbp), %rsi
    0x10000107f <+31>: movl   %eax, (%rsi)
->  0x100001081 <+33>: movl   -0x14(%rbp), %eax
    0x100001084 <+36>: movq   -0x10(%rbp), %rsi
    0x100001088 <+40>: movl   %eax, (%rsi)
    0x10000108a <+42>: popq   %rbp
    0x10000108b <+43>: retq  
複製代碼

指針的彙編

0x100001090 <+0>:  pushq  %rbp
    0x100001091 <+1>:  movq   %rsp, %rbp
    0x100001094 <+4>:  movq   %rdi, -0x8(%rbp)
    0x100001098 <+8>:  movq   %rsi, -0x10(%rbp)
    0x10000109c <+12>: movq   -0x8(%rbp), %rsi
    0x1000010a0 <+16>: movl   (%rsi), %eax
    0x1000010a2 <+18>: movl   %eax, -0x14(%rbp)
    0x1000010a5 <+21>: movq   -0x10(%rbp), %rsi
    0x1000010a9 <+25>: movl   (%rsi), %eax
    0x1000010ab <+27>: movq   -0x8(%rbp), %rsi
    0x1000010af <+31>: movl   %eax, (%rsi)
->  0x1000010b1 <+33>: movl   -0x14(%rbp), %eax
    0x1000010b4 <+36>: movq   -0x10(%rbp), %rsi
    0x1000010b8 <+40>: movl   %eax, (%rsi)
    0x1000010ba <+42>: popq   %rbp
    0x1000010bb <+43>: retq   
複製代碼

引用的彙編

引用的本質是個指針, 必須初始化, 長指針, 一經聲明不可改變. 相似於 int * const p.

new 和 delete

newdelete, 還有new[], delete[], 是用來代替mallocfree的, 二者之間不能串用.

int * p1 = (int *)malloc(sizeof(int));
int * p2 = new int;
*p2 = 100;
cout<<*p1<<" "<<*p2<<endl;
    
int **pp1 = (int **)malloc(sizeof(int *));
int **pp2 = new int *;
pp1 = pp2;
    
struct Stu {
    float score;
    char name[30];
    char sex;
};
    
Stu * ps1 = (Stu *)malloc(sizeof(Stu));
Stu * ps2 = new Stu;
cout<<sizeof(*ps1)<<" "<<sizeof(*ps2)<<endl;
複製代碼
0 100
36 36
複製代碼

以上是newmalloc的比較.

float * pf1 = (float *)malloc(10 * sizeof(float));
float * pf2 = new float[10]{1.2, 3.4};
for (int i = 0; i < 10; i++) {
    cout<<pf1[i]<<" "<<pf2[i]<<endl;
}

char ** pp = new char * [10];
for (int i = 0; i < 10; i++) {
    pp[i] = "Castiel";
}
pp[10] = nullptr;
while (*pp) {
    cout<<*pp++<<endl;
}

int (* p)[5] = new int[3][5];
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 5; j++) {
        p[i][j] = i + j;
    }
}
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 5; j++) {
        cout<<p[i][j]<<" ";
    }
    cout<<endl;
}

int (* p2)[3][5] = new int[2][3][5];
for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 3; j++) {
        for (int k = 0; k < 5; k++) {
            p2[i][j][k] = i + j + k;
        }
    }
}
複製代碼

對於連續的空間, 也就是數組來講, 咱們可使用new[], 來開闢堆內存.

0 1.2
0 3.4
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
Castiel
Castiel
Castiel
Castiel
Castiel
Castiel
Castiel
Castiel
Castiel
Castiel
0 1 2 3 4 
1 2 3 4 5 
2 3 4 5 6 
複製代碼

上述代碼的打印日誌.

int * p = new int;
delete p;
int ** pp = new int * [10];
delete []pp;
int (*ppp)[5] = new int[3][5];
delete []ppp;
複製代碼

deletedelete[], 就是釋放內存和連續的內存.

char ** p = new char * [10];
for (int i = 0; i < 10; i++) {
    p[i] = new char[10];
}
for (int i = 0; i < 10; i++) {
    delete []p[i];
}
delete []p;
複製代碼

釋放由內向外, 層級釋放.

try {
    double * pd[50];
    for (int i = 0; i< 50; i++) {
        pd[i] = new double[500000000000];
        cout<<i<<endl;
    }
} catch (bad_alloc & e) {
    cout<<"內存申請異常 "<<e.what()<<endl;
}
複製代碼
... cpp
C++(37659,0x100395340) malloc: *** mach_vm_map(size=4000000000000) failed (error code=3)
*** error: can't allocate region *** set a breakpoint in malloc_error_break to debug 內存申請異常 std::bad_alloc Program ended with exit code: 0 複製代碼

對於堆內存申請失敗的異常捕獲的第一種方式, try-catch, 貌似OC中不多用到, 由於OC能夠給空對象發送消息.

void newError( ) {
    cout<<"內存申請異常"<<endl;
    exit(1);
}

int main(int argc, const char * argv[]) {

    double * pd[50];
    set_new_handler(newError);
    for (int i = 0; i< 50; i++) {
        pd[i] = new double[500000000000];
        cout<<i<<endl;
    }
    return 0;
}
複製代碼
...
C++(37705,0x100395340) malloc: *** mach_vm_map(size=4000000000000) failed (error code=3)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
內存申請異常
Program ended with exit code: 1
複製代碼

第二種是使用set_new_handler回調函數來進行捕獲.

double * pd[50];
for (int i = 0; i< 50; i++) {
    pd[i] = new (nothrow)double[500000000000];
    if (pd[i] == nullptr) {
        cout<<"內存申請異常 "<<" "<<__FILE__<<" "<<__func__<<" "<<__LINE__<<endl;
    }
}
複製代碼
...
C++(37787,0x100395340) malloc: *** mach_vm_map(size=4000000000000) failed (error code=3)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
內存申請異常  /Users/zhushuangquan/Desktop/C++/C++/main.cpp main 19
複製代碼

第三種則是不進行異常捕獲...., 三種狀況優先使用try-catch.

inline 內聯函數

inline int sqr(int x) {
    return x * x;
}

int main(int argc, const char * argv[]) {

    int i = 0;
    while (i < 5) {
        printf("%d\n", sqr(i++));
    }
    return 0;
}
複製代碼

代替宏函數, 會在代碼段出現多個副本, 但取決於編譯器優化, 適用函數體小並被頻繁調用,

強制類型轉換

儘可能不要強轉, 強轉是設計不足致使的.

double d; int i;
d = static_cast<double>(i);
i = static_cast<int>(d);

d = static_cast<double>(10) / 3;
cout<<d<<endl;

void * p; int * q;
p = q;
q = static_cast<int *>(p);
複製代碼
3.33333
Program ended with exit code: 0
複製代碼

static_cast 隱式轉化

int * m; int n;
m = reinterpret_cast<int *>(n);
複製代碼

reinterpret_cast 指針與數值之間進行轉換

void foo(const int & a) {
    const_cast<int &>(a) = 200;
}

int main(int argc, const char * argv[]) {

    int a;
    const int & ra = a;
    a = 100;
    cout<<a<<endl;
    const_cast<int &>(ra) = 300;
    cout<<ra<<endl;
    cout<<a<<endl;

    const int * p = &a;
    *const_cast<int *>(p) = 400;
    cout<<*p<<endl;
    
    foo(a);
    cout<<a<<endl;
    
    return 0;
}
複製代碼

const_cast只做用與指針和引用, 去const

const int a = 100;
const int & ra = a;
    
const_cast<int &>(ra) = 200;
cout<<a<<endl;
cout<<ra<<endl;
複製代碼
100
200
複製代碼

對於const修飾的值, 是不能改變的,

命名空間

對於OC是用前綴, 對於Java是用包名, 對於Swift也有和C++同樣的命名空間.

void foo() {
    cout<<"foo"<<endl;
}

int mm = 100;

int main(int argc, const char * argv[]) {

    std::cout<<::mm<<endl;
    ::foo();
    return 0;
}
複製代碼

::全局無名命名空間.

namespace ONE {
    int x = 4;
}

namespace ANOTHER {
    int x = 14;
}

int main(int argc, const char * argv[]) {
    
    {
        int x = 250;
        cout<<ONE::x<<endl;
        cout<<ANOTHER::x<<endl;
        cout<<x<<endl;
    }
    
    {
        using ONE::x;
        cout<<x<<endl;
    }
    
    {
        using namespace ANOTHER;
        cout<<x<<endl;
    }
    return 0;
}
複製代碼
4
14
250
4
14
Program ended with exit code: 0
複製代碼

命名空間的使用, 命名空間只能定義在全局.

第一種推薦使用, 第二種少用, 第三種禁用.

namespace ONE {
    int x = 4;
    namespace ANOTHER {
        int x = 14;
    }
}

int main(int argc, const char * argv[]) {

    cout<<ONE::ANOTHER::x<<endl;
    return 0;
}
複製代碼
14
Program ended with exit code: 0
複製代碼

命名空間的嵌套.

namespace ONE {
    int a = 4;

}
namespace ONE {
    int b = 14;
}

int main(int argc, const char * argv[]) {

    using namespace ONE;
    cout<<a<<" "<<b<<endl;
    return 0;
}
複製代碼

同名命名空間自動合併.

string

字符串是C++C高級的地方, 操做起來也比C簡單太多, 也比OC簡單太多, 問OC爲啥那麼麻煩.

int * pi = new int(10);
cout<<pi<<endl;
cout<<*pi<<endl;

string * p = new string("Castiel");
cout<<p<<endl;
cout<<*p<<endl;

char * q = "Castiel";
cout<<q<<endl;
cout<<*q<<endl;
複製代碼
0x100506740
10
0x10050c710
Castiel
Castiel
C
Program ended with exit code: 0
複製代碼

雖然string是一種類, 但已經能夠和int的地位相同了.

string s;
cout<<sizeof(string)<<endl;
cout<<sizeof(s)<<endl;

string s1("Castiel");
string s2 = "Castiel";
cout<<s1<<" "<<s2<<endl;

cin>>s;
cout<<s<<endl;

getline(cin, s); //解決了空格的問題
cout<<s<<endl;

string s3 = "Great Wall";
cout<<s3.size()<<endl;

string s4 = " in China";
cout<<(s3 += s4)<<endl;

string s5 = "Great Wall";
if (s3 == s5) {
    cout<<"=="<<endl;
} else {
    cout<<"!="<<endl;
}

string s6;
cout<<(s6 = s3)<<endl;

string s7 = to_string(1234);
cout<<s7<<endl;

string s8 = "123abc";
cout<<stoi(s8)<<endl;

複製代碼
24
24
Castiel Castiel
10
Great Wall in China
!=
Great Wall in China
1234
123
Program ended with exit code: 0
複製代碼

上述是string的基本使用, 沒啥技術含量.

class

C++中, 結構體和類的本質沒有什麼具體的區別, 只是權限訪問上有些許不一樣, 而不是像之前認爲的類是引用傳遞, 而結構體是值傳遞, 傳地址, 不也是值麼, 只不過能取地址... 笑.

struct Date {
    
    void init(int year = 1970, int month = 01, int day = 01) {
        _year = year;
        _month = month;
        _day = day;
    }
    
    void printDate() {
        cout<<_year<<"-"<<_month<<"-"<<_day<<endl;
    }
    
private:
    int _year;
    int _month;
    int _day;
};
複製代碼

struct默認所有是public.

class Date {
    
    int _year;
    int _month;
    int _day;
    
public:
    void init(int year = 1970, int month = 01, int day = 01) {
        _year = year;
        _month = month;
        _day = day;
    }
    
    void printDate() {
        cout<<_year<<"-"<<_month<<"-"<<_day<<endl;
    }
};
複製代碼

class默認所有是private.

int main(int argc, const char * argv[]) {

    Date * date = new Date;
    date->init(1992, 06, 19);
    date->printDate();
    delete date;

    Date * date2 = new Date;
    date2->init(2012, 12, 21);
    date2->printDate();
    delete date2;

    return 0;
}
複製代碼
1992-6-19
2012-12-21
(lldb) p/x date
(Date *) $0 = 0x000000010050e9c0
(lldb) p/x date2
(Date *) $1 = 0x000000010050f2b0
(lldb) 
Program ended with exit code: 0
複製代碼

從打印上來講, 結構體和類是同樣的, 能夠說類的本質就是結構體指針, 但其實類也能夠不用指針引用, 就變成了值傳遞? 又笑.

class Date {
    
private:
    int _year;
    int _month;
    int _day;
    
public:
    Date(int year = 1970, int month = 01, int day = 01);
    ~Date();
    void printDate();
};

Date::Date(int year, int month, int day)
:_year(year), _month(month), _day(day){}

Date::~Date() {
    cout<<"delete: "<<_year<<"-"<<_month<<"-"<<_day<<endl;
}

void Date::printDate() {
    cout<<_year<<"-"<<_month<<"-"<<_day<<endl;
}

int main(int argc, const char * argv[]) {
    
    Date * date = new Date(1992, 06, 19);
    date->printDate();
    delete date;
    
    Date * date2 = new Date(2012, 12, 21);
    date2->printDate();
    delete date2;
    
    return 0;
}
複製代碼
1992-6-19
delete: 1992-6-19
2012-12-21
delete: 2012-12-21
Program ended with exit code: 0
複製代碼

class多文件的基本用法, 構造器, 析構器, 參數列表什麼的就很少說了.

namespace xxx {
    int a;
    void log();
}

void xxx::log() {
    a = 120;
    cout<<a<<endl;
}
複製代碼

其實類名本質就是一個命名空間. 怎麼又是命名空間了呢, 再笑....

最後

更多新鮮文章能夠關注並Star, 咱們一塊兒學習.

GitHub Repo:coderZsq.github.io
Follow: coderZsq · GitHub

相關文章
相關標籤/搜索