C++零散知识笔记本
- 1.符号
- 1.1符号输出
- 1.1.1 转义序列
- 格式
- 符号
- 不常用
- 1.1.2 泛化转义序列
- 1.1.1 转义序列
- 1.2运算符
- 1.1符号输出
- 2.基本内置类型 wchar_t
- 3.内置类型所占字节数
- 内置类型的简写
- 4.变量的本质
- 变量与指针的故事
- (1)malloc函数
- (2)new关键字
- 5.全局变量/局部变量
- 变量的静态(static)修饰
- 类内成员的静态(static)修饰
- 静态成员变量
- 静态成员函数
- 6.常量
- 6.1常量的修饰符
- 整型字面值常量(后缀)
- 浮点型字面常量(后缀)
- 字符和字符串字面值(前缀)
- 6.2布尔常量的值
- 6.3宽字符常量
- 6.4 const常量
- 修饰变量名时
- 修饰函数时
- 6.1常量的修饰符
- 7.派生类到基类的类型转换
- 8.纯虚函数和抽象类
- 9.派生访问说明符 public、protected、private
- protected和private的区别
- 10.getchar()
- 11.内联函数(inline)
- 12.p++/++p/(p)++/++(p)
- 12.1 p++/++p
- 12.2 (p)++/++(p)
- 13. sizeof()和strlen()
1.符号
1.1符号输出
%
需要用%%
1.1.1 转义序列
格式
\n 换行
\t 横向制表符 \v 纵向制表符
\r 回车
符号
反斜线 \\
问号 \?
双引号 \" 单引号\'
不常用
响铃(报警)符 \a
退格符 \b
进纸符 \f
1.1.2 泛化转义序列
\x 后跟1~n个16进制数字
\x4d 输出M
\x1234 输出4个16进制数对应的字符
\ 后跟1~3个8进制数字
\115 输出M
\1156 输出M6(8进制时\最多和前3个数字结合)
1.2运算符
++/--是单目运算符,只能对一个变量进行操作,不能对常量和表达式进行操作
a?b:c是多目运算符
左值指的是变量;表达式的返回值以及常量在内存中都没有具体的地址,是右值。
2.基本内置类型 wchar_t
宽字符型 wchar_t
因为有typedef short int wchar_t
故wchar_t是short int的别称
cin和cout将输入和输出看作是char流,因此不适合用于处理wchat类型,iostream头文件提供了wcin 和wcout用于处理输入输出流。用前缀L来表示宽字符常量和宽字符串
wchar_t wStr[] = L"我使用了wcha_t类型";
3.内置类型所占字节数
char 1
short int/wchar_t 2
int/float/ 4
long int/double 8
long double 16
内置类型的简写
short/long/unsigned/signed int a
可以省略int,简写为
short/long/unsigned/signed a
4.变量的本质
是程序可操作存储区的名字,其类型决定了这块存储器能保存多大的数据,这些数据是按什么顺序被存储的以及能进行什么操作。
运算符是对这些存储区进行操作的工具。
变量与指针的故事
参考:malloc的用法和意义
何时使用或何时不使用malloc函数
我们声明一个指针,让它指向内存的一个区域,一般的做法是声明一个变量,然后用指针指向这个变量
int a[5];
int *p=a;
如果不想为此专门声明一个变量,则可以用函数malloc()
和关键字new
来进行内存的动态分配
。
在使用完毕后,需要对应free()
和delete
来释放这一部分内存
(1)malloc函数
malloc(大小)
在内存上分配指定大小的一块区域
int *p;
p=(int*)malloc(sizeof(int));
或者:int *p=(int*)malloc(sizeof(int));
其中(int*)
是将malloc()
返回的void*
指针强制转换成目标指针。
(2)new关键字
new
要更智能一些,但malloc()
也有它的优点,参见:
int *p=new int;
new不用强制类型转化,也不用调用sizeof()计算大小,这两步都由它自动识别。
new
可以调用类对象的默认构造函数进行初始化,以及内置类型的默认初始化
int *q=new int[2](); //内置类型int,后面加()可默认初始化int数组内容都为0
class A{/* */};
int *q=new A; //自定义类,后面不用加()
5.全局变量/局部变量
- 当全局变量和局部变量重名时,函数内部优先使用局部变量
- 全局变量定义后会被自动初始化,局部变量需要手动初始化
- 全局变量存储在静态存储区(全局存储区,编译时便分配空间),局部变量存储在内存的栈区(使用时才分配空间,函数返回后被清除)
变量的静态(static)修饰
对于全局变量
- 全局变量默认为extern的,能在整个工程内可见;静态全局变量缩小了范围,只在当前文件可见。
对于局部变量
- 静态局部变量存储在静态存储区(全局存储区),所以生存周期为整个程序,不会因为函数返回而被清除;
- 仍然只对函数内部可见
总结:
- 全局变量(extern)作用于整个工程
- 静态全局变量作用于单个文件
- 静态局部变量作用于单个文件,但只函数内部可见
以上三者均为静态存储,生存周期为整个程序,但作用域不同
- 局部变量作用于单个文件,且只函数内部可见;存储在内存栈区,生存周期短。
类内成员的静态(static)修饰
参考:(C++中类的(static)静态成员变量与(static)静态成员函数)[https://blog.csdn.net/lms1008611/article/details/81408236]
- 静态成员(变量/函数)同样受public/private/protected访问控制符影响;
- 在全局存储区存储,故基类和所有派生类共有一个静态成员(变量/函数);
- 可以通过类名直接访问;
静态成员变量
静态成员变量需要在类外单独分配空间,即在类外定义,同时初始化;
class A{
public:
static int a;
};
...
int A::a=10; //在类外用类名调用来初始化
...
int main(){
}
但常量(const)静态成员是个特例(const常量必须在定义时初始化),可以在类内声明时定义并初始化,也可以在类外定义并初始化;
class A{
public:
const static int a=1+2; //可以用常量表达式初始化
const static char b ='1'+'2'; //'1'和'2'的ASCII码相加再转换成char,返回49+50=99,对应c
};
只用这两个类型可以如此。
静态成员函数
- 静态成员函数属于整个类所有,没有this指针
- 可以通过类名/对象名直接访问类的公有静态成员函数
- 静态成员函数只能直接访问静态成员变量和静态成员函数(大家都在全局存储区,不会中途消失;const也只能和const限定过的打交道,是因为const都不会修改数据)
6.常量
6.1常量的修饰符
E/e 10^
整型字面值常量(后缀)
12L/12l long
12LL/12ll long long
12U/12u unsigned
浮点型字面常量(后缀)
1.2f/1.2F float
1.234l/1.234L long double
字符和字符串字面值(前缀)
注意:不同编译器可能要求不同
u'a'/u"abc" Unicode 无符号16字符 char16_t
U'a'/U"abc" Unicode 无符号32字符 char32_t
L'a'/L"abc" 宽字符 wchar_t
u8“abc” UTF-8 char(仅用于字符串常量)
6.2布尔常量的值
bool
只占一个字节
true
真,1
false
假,非0,bool转换为int时为0
注意大小写,大写的BOOL
不是bool
类型而是int
,在一些编译器里
#define int BOOL //因为是int所以占四个字节
#define TRUE 1
#define FALSE 0
6.3宽字符常量
需要使用大写L
,否则就是窄字符
wchar_t a=L'xx';
6.4 const常量
修饰变量名时
1.必须在定义时初始化
2.const int i=10;
等价于#define i 10
,不同点在于有类型int的信息
修饰函数时
函数不是普通函数,需要是类的成员函数
class a{
public:
int fun()const
{
}
}
const函数不能修改类的数据成员;
又因为非const函数可能修改类的数据成员,所以为了避免这种事发生,const函数不能调用其他非const函数
但加上mutable修饰符的数据成员,对于任何情况下通过任何手段都可修改,自然此时的const成员函数是可以修改它的
7.派生类到基类的类型转换
基类指针/引用指向派生类对象,实际是指向了派生类中继承自基类的那一部分。这一过程中发生了派生类到基类的类型转换,但不存在基类到派生类的类型转换。
用派生类对象给基类初始化或者赋值时,基类只接收派生类中继承自基类的那一共有部分。
8.纯虚函数和抽象类
纯虚函数的作用在于提出一个概念,由派生类来对这个概念进行具体展开(多态)。
因为概念本身是抽象的,所以含有纯虚函数的类叫抽象类,这样的类无法实例出对象,从而保证其本身的抽象性。(实例化出来的对象是个空壳概念,没有实际意义)
派生类需要对这个继承来的纯虚函数进行覆盖,来赋予抽象以具体。如果没有覆盖,则派生类仍是一个抽象类。
9.派生访问说明符 public、protected、private
参考:
- 这三种说明符对于派生类成员(及友元)对直接基类的成员访问权限没有影响,不管是怎么继承的,直接基类的public和protected成员对于派生类成员(及友元)都是可见的,private是不可见的。
- 区别在于派生类的对象对于直接基类成员的访问权限。因为类的private和protected成员本来就不对类的对象可见,所以区别只在于基类的public成员。只有当时public继承时,基类的public成员才对派生类对象可见。
结论:
单次派生下
- 无论何种继承方式,派生类定义的内部都可以使用直接基类的public和protected成员;
- 在不继承的情况下,类的对象只能访问类的public成员。当发生继承且仅当是public继承时,派生类对象才能在访问派生类public成员的基础上,还可以访问基类的public成员。
结论精简版:
单次派生下,对于派生类
- 成员仅不可见基类private成员;
- 对象仅在public继承时可多见基类public成员
protected和private的区别
个人感觉protected继承是对private继承的一种补充,在派生一次的情况下,两者对访问权限没有影响。
私有继承时基类中各成员属性均变为private,并且基类中private成员被隐藏
保护继承时基类中各成员属性均变为protected,并且基类中private成员被隐藏
这一属性的区别体现在再次派生的时候。
若A-->B-->C
B私有继承自A后,A的可见成员信息便止步于此(因为变成了B的私有成员),无法影响接下来的继承;
B保护继承自A后,A的可见成员信息作为B的保护成员,还可以被继续继承下去。
10.getchar()
键盘输入后保存在缓存区,当用户键入回车之后,getchar才开始从stdio流中每次读入一个字符,包括回车键。
返回类型为int,返回值为ASCII码或EOF(-1),用char接收时则会转换成字符。
while(char a=getchar()){}
此时while无法停止,因为只有当getchar返回0时while才能停止,所以即使读到文件末尾的\0
返回回来的EOF(-1)
也还是会让循环继续,更不用说回车代表的换行符\n
对应ASCII 10了。而ASCII码中的0对应的NULL我没有找到对应的输入方法,摊手~
11.内联函数(inline)
参考:
- 在内联函数内不允许用结构语句,即循环语句和开关语句,如果内联函数有这些语句,则编译将该函数视同普通函数那样产生函数调用代码
- 递归函数不能做内联函数
- 内联函数只适合于只有1~5行的小函数。对一个含有许多语句的大函数,函数调用和返回的开销相对来说微不足道,所以也没有必要用内联函数实现
- 内联函数的定义必须出现在内联函数第一次被调用之前
- 类内定义的函数是内联函数,多用来返回private和protected数据成员的值
12.*p++/*++p/(*p)++/++(*p)
12.1 *p++/*++p
int a[4] = { 0, 1, 2, 3 };
int *p = a;
因为后置++
的优先级大于*
,前置++
的优先级和*
相同但遵循从右向左依次运算,
所以*p++
即*(p++)
,*++p
即*(++p)
值得注意的是:
综合后置
++/--
的特点,即先取值后运算,*(p++)
在进行*
操作时使用的p
的值是++
前的值,类似于先*p
再执行p++
故*p++
输出0
而按照常理,*++p
输出1;
12.2 (*p)++/++(*p)
括号只比作用域::
优先级低,所以优先括号里面的取值,即a[0]++/++a[0]
结果是0/1
13. sizeof()和strlen()
sizeof(类型/对象)
是关键字,也是运算符,不是函数。返回一个对象或者类型所占的内存字节数,返回值是unsigned int类型;
strlen (字符数组名)
是函数。计算字符串s的(unsigned int型)长度,不包括'\0'在内
怎样计算C++继承、虚继承、虚函数类的大小