Cpp Basic - xapool/xapool.github.io GitHub Wiki
- C++ 中可以使用 1 0 表示真假,0 为假,非 0 为真
- C/C++ 中一般返回 0,代表函数执行成功
- 注意 strcmp 比较字符串时,若字符串相同,返回的是 0 值
- 存储其它变量地址的变量,称之为指针变量
- 在声明一个指针变量的时候,在此变量前使用的星号,仅仅是表明其是一个指针变量,并不具有以下意义,因此可以直接使用
&
取地址符进行直接赋值 - 指针变量在使用的过程中,加不加星号的含义不同
- 星号后面跟指针名称表示指针所指向内存单元中的内容,即值
- 不加星号表示指针本身的值,是它自身内存单元中存储的内存地址,用于改变指针所指对象,常与
&
取地址符一起使用
- 当指针变量是一个 char 类型时,这个指针其实指向的是一个 char 类型数组,是一个数组指针,相当于一个字符串指针,可当做数组一样使用
- 常量指针和指针常量,容易混淆,不要看中文翻译,直接使用英语
// 从右向左读,p is a [?] pointer point to ....,* 号读为 point to int const *p; // p is a pointer point to const int const int* p; // p is a pointer point to int const,同上 // 以上,pointer to const,从右往左看,都是 * 号在 const 右边 int *const p; // p is a const pointer point to int const int * const p; // p is a const pointer point to int const int const * const p; // p is a const pointer point to const int,同上 // 以上,const pointer,从右往左看,都是 * 号在 const 左边
- pointer to const(常量指针),不能通过 *p 来改变所指向对象的值,p 的值可以改变
- const pointer(指针常量),并不能改变 p 的值,即改变指针所指对象,指针本身是常量,因此需要在声明时就进行初始化,不能执行自增,自减操作。*p 的值可不可以改变得看 point to 的是不是一个常量
- 给数组同时赋多个值只有在数组初始化时,也就是在声明数组时,才是合法的,以后使用时只能给数组中的单个元素进行赋值
- 访问数组中元素,中括号也叫位移(offset)操作符,相当于在指针中的地址上加上括号中的数字。例如,下面两个表达式互相等价
a[5] = 0; // a [offset of 5] = 0 *(a+5) = 0; // pointed by (a+5) = 0,无论 a 是一个指针还是一个数组名,这两个表达式都合法
- char 类型数组,还可以使用字符串常量来进行初始化。注意由双引号引起来的字符串末尾总是会被自动加上一个空字符
\0
- 数组名在大部分情况下会隐式转换为 const pointer(指针常量),来当做指针使用(但并不是一个真正的指针,在代码执时并不像指针一样有自己的内存单元,因为在编译器编译时就直接把数组名转化为了一个固定地址,并在符号表中做好了映射),除了以下几种情况:
- sizeof 运算时
- & 符号取址时
- 用字符串常量初始化字符数组时
- C11 的 _Alignof 运算符
- char 类型数组可以以数组名来用 cout 输出数组全部内容,而普通数组不行。因为 C/C++ 不进行数组的边界检查,数组在内存中存放的只是所有数组元素的值,而不存在一个地方可以表示数组的大小。所以 cout 函数没法知道该输出多少个元素。但字符串则不同,它有一个
\0
用来表示字符串结束,并且每一个元素都是一个字节,cout 看到\0
就知道输出结束了,但如果没有这个结束符就会出现乱码 - 指针数组和数组指针,语义上重点在后面的词,类似常量指针和指针常量
int *a[5]; // 指针数组,每个元素都是一个指针,[]优先级高 int (*p)[5]; // 数组指针,指针指向一个数组 char *p = "Hello"; // 同上
- 指针数组是一个数组,该数组内的每一个元素都是指针,也就是用来存储指针的数组
- 数组指针一定是一个指针,该指针指向一个数组,也就是数组的第一个元素,该指针的值等于数组第一个元素的地址值,其中存放的是数组第一个元素
- 指针函数和函数指针
int *f(int,int) {} // 顾名思义,指针函数是一个函数,该函数的返回类型为整型指针 int (*f)(int,int); // 函数指针是一个指针,该指针指向的是一个函数,可以通过该指针调用函数
- 变量被声明时后面的冒号,是表示该变量占几个 bit 空间
- 构造函数后面的冒号起分割作用,是类给成员变量进行赋值的方法,用于初始化成员变量,初始化的顺序应与声明的顺序保持一致
-
public:
和private:
后面的冒号,表示后面定义的所有成员都是公有或私有的,直到下一个public:
或private:
出现为止。若不写的话,private:
做为默认,这点不同于 Java - 类名后面的冒号是用来定义类的继承,
class 派生类名 : 继承方式 基类名
,继承方式可为 public、private 和 protected,默认是 public
- 表示 "域操作符",例如,头文件中声明了一个类 A,A 中声明了一个成员函数 void f(),但没有在类的声明里给出 f 的定义(实现),那么在类外定义 f 时,
就要写成
void A::f()
,表示这个 f() 函数是类 A 的成员函数 - 表示引用成员函数及变量,作用域成员运算符,例,
System::Math::Sqrt()
相当于System.Math.Sqrt()
- 一般还有一种用法,就是直接用在函数名前,表示是调用的是全局函数,非成员函数
- C++ 中当定义类对象是指针对象时候,就需要用到
->
指向类中的成员;当定义一般对象时候时就需要用到.
指向类中的成员。访问结构体中的成员元素也一样
例:
int m;
int &n = m;
n是m的一个引用(reference),m是被引用物(referent)。n相当于m的别名,对n的任何操作就是对m的操作。所以n既不是m的拷贝,也不是指向m的指针,其实n就是m它自己,在内存中是同一个内存地址
- 引用的规则
- 引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)
- 不能有 NULL 引用,引用必须与合法的存储单元关联(指针则可以是NULL)
- 一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)
- C++ 语言中,函数的参数和返回值的传递方式有三种:值传递、指针传递和引用传递。 而 Java 中没有指针传递这一项,只有值传递和引用传递,其中的引用传递类似于 C++ 的指针传递
&
作为取地址用的时候,常和指针联系在一起,赋值给指针变量,因为指针就是用来存放地址的
- C++ 并没有局部内部类,也就是不能在函数中定义类。并不像 Java 一样区分了普通内部类、静态内部类、匿名内部类,只是一个简单的内部类
- C++ 的内部类对象没有持有外部类对象的指针,不能访问外部类对象的非静态成员,类似于 Java 的静态内部类,而 Java 的非静态内部类对象有外部类对象的指针,能访问外部类对象的非静态成员
- C++ 多态的表现和 Java 的上下转型不一样,而是使用强制类型转换操作符。而类似于 Java 的向下转型的写法是为了在一段分配好的空间 上(栈或堆中)调用这个类的构造函数,从而真正的创建一个对象(placement new),可以提高长时间运行的应用程序的性能,减少在堆中进行内存分配的时间
A a; // 等价于 A a = A()
A a(1); // 等价于 A a = A(1)
A * a = new a(); // 不要忘记不使用时要 delete a
C++ 中对象的实例化可不通过 new 来进行,但使用 new 与否是有区别的。
- 不使用 new,是在栈上给对象分配内存,不需要手动释放内存
- 使用 new,是在堆上动态分配内存,使用完须手动 delete 销毁,显然频繁调用场合并不适合 new,就像 new 申请和释放内存一样
- 销毁对象:当一个对象使用完毕,可以显式的调用类的析构函数进行销毁对象。但此时内存空间不会被释放,以用于其它对象的实例化
- 释放内存,
- 如果缓冲区在堆中,那么调用
delete type_pointer
可进行内存的释放 - 若在栈中,则在其作用域内有效,跳出作用域,内存自动释放
- 如果缓冲区在堆中,那么调用
除了使用 IDE 进行调试外,当然也都可以使用命令行进行调试。C 就是使用 gdb 来调试,Java 是使用 jdb 来调试。在 Mac 上 C 还可以使用 lldb 来调试,若使用 gdb 还需要单独安装并签名。在编译时都需要加上 -g
参数,以便程序包含调试信息。最后以调试工具启动程序,gdb test
。使用方法类似,都是下断点,运行,调试,具体命令查看各自工具的 help。
声明类
- 左移运算是将一个二进制位的操作数按指定移动的位数向左移动,移出位被丢弃,右边移出的空位一律补0。在数字没有溢出的前提下,对于正数和负数,左移一位都相当于乘以2的1次方,左移n位就相当于乘以2的n次方
- 右移运算是将一个二进制位的操作数按指定移动的位数向右移动,移出位被丢弃,左边移出的空位一律补0,或者补符号位,这由不同的机器而定。在使用补码作为机器数的机器中,正数的符号位为0,负数的符号位为1。右移一位相当于除2,右移n位相当于除以2的n次方 例:
1 << 0; //是把 1 的二进制 左移 0 位,结果还是 1,二进制 0000 0001
1 << 1; //是把 1 的二进制 左移 1 位,结果是 2,二进制 0000 0010
- 关于变量声明时未进行赋值,这点 C++ 和 Java 一样,做为成员变量,都会给一个默认值。而局部变量,在 C++ 中会是随机的,因为在桟中的内存是反复使用的。需注意在 Java 中若声明时未赋值并且还进行了调用,则会编译报错未初始化变量
- 在 C 中,NULL 和 0 的值都是一样的,NULL 常用于指针和对象,0 用于数值。但也有些系统不将 0 地址作为 NULL,而是用其它的地址
- 关于栈大小
- 64位上 linux 内核栈是 16KB,32位上是 8KB
- 用户态的桟大小,可用
ulimit -a
查看,ulimit -s
设定,Windows 下和编译器配置有关 - Java 中的栈大小可以通过 JVM 的
-Xss
参数来指定,默认是 1M - Java 中每个方法的桟帧大小在编译期就已经确定好了,运行期间并不会改变。C++ 中也可以动态操作,如在栈上实例化对象
- 没有出口的递归调用,容易造成 StackOverflow
- 翻译问题
- A handle is an abstract reference to a resource. Handle 是对某个资源的抽象引用
- A handler is an asynchronous callback subroutine. Handler 则是一个异步的回调函数(子程序)