- GitHub Repo:coderZsq.github.io
- Follow: coderZsq . GitHub
- Resume: coderzsq.github.io/coderZsq.we
日常扯淡
作为 iOS 开发的菜鸡, 平日里的工作就是做业务, 调 UI, 对于我们这种弱鸡玩家来说, 编程呢, 其实就是调方法, 调属性, 调库...
但光是做业务 UI 的工作肯定会让自己日渐乏味, 为了不重复写那些看了想吐的代码, 去年就花了点时间写了一个代码生成工具, 用于配置一键生成垃圾代码的, 这样对于菜鸡开发者也就是我来说, 只需要调调自己封装的一些 UI 库, 搭搭积木就完成工作了, 其他时间就可以自由支配玩点有趣的事情.
去年浑浑噩噩, 学了一堆有的没的, 什么 Vue, React, Spring 的调用, 但一直这样无所事事的我, 今年也想深入的学习一些什更深层次的东西, 而不仅仅只是在 UI 层的浅尝辄止.
然而在 iOS 这个领域中, 想要深入研究, C\C++, 汇编, Linux, 像三座大山一样拦在我面前, 所以做为菜鸡的我路漫漫而其修远兮.
本文就通过学习 C++ 的语法开始, 一点一点的剖析 OC 的本质, 直到世界的尽头.
C++ 作者的建议
在 C++ 中几乎不需要用宏, 用 const 和 enum 定义显式的常量, 用 inline 避免函数调用的额外开销, 用模板去刻画一组函数或类型, 用 namespace 去避免命名冲突.
不要在你需要变量之前去声明, 以保证你能立即对它进行初始化.
不要用 malloc, new 运算会做的更好.
避免使用 void*, 指针算数, 联合和强制, 大多数情况下, 强制类型转换是设计错误的指示器.
尽量少用数组和 C 风格的字符串, 标准库中的 string 和 vector 可以简化程序.
更加重要的是, 试着将程序考虑为一组由类和对象表示的互相作用的概念年, 而不是一堆数据结构和一些可以拨弄的二进制.
标准输入输出
我们先来看 C++ 的标准输入输出:
- char buf[10];
- scanf("%s", buf);
- printf("%s\n", buf);
这个是大家都很熟悉的 C 的输入输出, 也就是 scanf 和 printf. 但 C 语言是越界不检的, 所以这里的 char buf[10] 的缓冲区可能会造成写越界, 导致不安全访问.
- char buf[10];
- fgets(buf, 10, stdin);
- printf("%s\n", buf);
所以 C 使用了 fget 强制的截断了输入来保证, 访问的安全.
- 12345678901234567
- 123456789
- Program ended with exit code: 0
以上是 C 语言安全标准输入的打印日志, 我们可以看到, 由于设置了
10
为输入截断参数, 后面就不再输入了.
- char buf[10];
- cin>>buf;
- cout<<buf<<endl;
我们再来看看 C++ 的输入流 cin 和 cout, 可以看到的是, 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;
使用 setw 和 setiosflags 来设置域宽和左右对齐.
#include <iomanip>
在使用 setw 和 setiosflags 之前需要添加头文件.
- 1234
- 1234
- 1234
- Program ended with exit code: 0
以上就是使用 setw 和 setiosflags 的打印结果.
- 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
new 和 delete, 还有 new[], delete[], 是用来代替 malloc 和 free 的, 两者之间不能串用.
- 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
以上是 new 和 malloc 的比较.
- 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;
delete 与 delete[], 就是释放内存和连续的内存.
- 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
来源: https://juejin.im/post/5aa363b5f265da237e0953e1