C++零散知识笔记本

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()也有它的优点,参见: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

参考:C++中公有继承、保护继承、私有继承的区别

  • 这三种说明符对于派生类成员(及友元)对直接基类的成员访问权限没有影响,不管是怎么继承的,直接基类的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. 在内联函数内不允许用结构语句,即循环语句和开关语句,如果内联函数有这些语句,则编译将该函数视同普通函数那样产生函数调用代码
  2. 递归函数不能做内联函数
  3. 内联函数只适合于只有1~5行的小函数。对一个含有许多语句的大函数,函数调用和返回的开销相对来说微不足道,所以也没有必要用内联函数实现
  4. 内联函数的定义必须出现在内联函数第一次被调用之前
  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++继承、虚继承、虚函数类的大小

posted @ 2019-11-12 17:06  中庭之园  阅读(312)  评论(0编辑  收藏  举报