c++/c 知识点梳理(1)

1.逻辑表达式短路

&&(与) 优先级高于 ||(或),且x||y 若判断x为真,则返回真,不去判断y;
而 x && y 若判断x为假,则返回假,不会去执行y

//自增自减的情况下需额外注意
x < y || ++x < ++y - 1 

2.引用与指针的区别

指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。

3. static关键字

static静态变量作用主要有两种,第一是限定作用域,第二是保持变量内容持久化。该变量存放在静态存储区。全局静态变量作用域,整个程序运行期间会一直存在;局部静态变量作用域,仍为局部作用域,当定义它的函数或者语句块结束的时候,作用域结束。但是当局部静态变量离开作用域后,并没有销毁,而是仍然驻留在内存当中,只不过我们不能再对它进行访问,直到该函数再次被调用,并且值不变;
静态函数,其定义和生命在默认情况下都是extern,但是静态函数只是在声明它的文件中可见,不能被其它文件所见。
静态变量只会初始化一次,第二次则会跳过定义语句

4.指针数组和数组指针

主要是弄清楚* 和 [] 与谁搭配:
char* a[10]:指针数组,10个元素,每个元素为指针,指向char型。可以理解为 char * [10],即10个char *,a为该数组的变量名 。

char (*a)[10]:数组指针,一个数组,10个元素,每个元素为char型,a是指针,指向该数组的首地址。可以理解为 char [10],10个char, *a指向该数组,a即为指针。

5.类型转换机制

i).c中的类型转换

主要有两种:(new-type) expression 和 new-type (expression)

ii).c++中的类型转换

类型转换可以分为两种:隐式类型转换显示类型转换(强制类型转换)
隐式类型转换比较常见,混合类型表达式运算中经常用到,如牛客网题目:

设有说明:char w;int x;float y;double z;
则表达式w*x+z-y 值的数据类型为 double。说法是否正确?
答:正确。类型优先级低的转化为优先级高的(范围小的转化为大的),
char < unsigned char < short < unsiged short < int < ...
unsigned int < long < unsigned long < float < double

显示转换(强制转换)有四种转换操作符:static_cast、dynamic_cast、const_cast、reinterpret_cast
static_cast:编译时期的静态类型检测。功能:完成基础数据类型;同一个继承体系中类型的转换;任意类型与空指针类型void*之间的转换。

  • 用法:static_cast < type-id > ( expression )
  • 该运算符把expression转换为type-id类型,在编译时使用类型信息执行转换,在转换执行必要的检测(指针越界,类型检查),其操作数相对是安全的。但没有运行时类型检查来保证转换的安全性。

dynamic_cast::dynamic_cast是在运行时检测。功能:使用多态的场景,增加了一层对真实调用对象类型的检测。

  • 用于在集成体系中进行安全的向下转换downcast(当然也可以向上转换,但没必要,因为可以用虚函数实现)
    即:基类指针/引用 -> 派生类指针/引用
    如果源和目标没有继承/被继承关系,编译器会报错!
  • dynamic_cast是4个转换中唯一的RTTI(运行时类型识别:指程序能够使用基类的指针或引用来检索其所指对象的实际派生类型,包括typeid操作符和dynamic_cast操作符)操作符,提供运行时类型检查。
  • dynamic_cast不是强制转换,而是带有某种”咨询“性质的,如果不能转换,返回NULL。这是强制转换做不到的。
  • 源类中必须要有虚函数,保证多态,才能使用dynamic_cast(expression)

reinterpret_cast: .interpret_cast是为了映射到一个完全不同类型的意思,这个关键词在我们需要把类型映射回原有类型时用到它。(转换最不安全)

const_cast:去除const常量属性,使其可以修改。volatile属性的转换 :易变类型<->不同类型

dynamic_cast将一个基类对象指针(或引用)cast到继承类指针,dynamic_cast会根据基类指针是否真正指向继承类指针来做相应处理, 即会作一定的判断。 对指针进行dynamic_cast,失败返回null,成功返回正常cast后的对象指针; 对引用进行dynamic_cast,失败抛出一个异常,成功返回正常cast后的对象引用。 
reinterpret_cast这个转换是最“不安全”的,两个没有任何关系的类指针之间转换都可以用这个转换实现。
static_cast静态转换是最接近于C风格转换,很多时候都需要程序员自身去判断转换是否安全。
const_cast这个转换好理解,可以将常量转成非常量。

在同一继承体系中,upcast(向上转换,即子类转成父类)是没有问题的,因为父类的行为都包含在子类里。而downcast(向下转换)有可能出现问题,编译时可能不会被发现。
引用与指针一个重要区别是,引用必须被初始化

6.异质链表

异质链表,是一种每个节点可以存储不同类型对象的链表。其实现的关键技术是模板元编程和多态性。
如果将这些不同类型的对象用链表进行直接链接,显然不太好操作,但是我们可以抽象出这些对象的共同点, 将这些共同点构造成结点, 然后把这些结点串起来。 并且需要保证, 通过这些结点, 可以访问到对应的对象,也就是用抽象类指针构造派生类对象链表

7.c的格式化输入输出

% - 0 m.n l或h 格式字
  ①%:表示格式说明的起始符号,不可缺少。
  ②-:有-表示左对齐输出,如省略表示右对齐输出。
  ③0:有0表示指定空位填0,如省略表示指定空位不填(右对齐时才左边补0)。
  ④m.n:m指域宽,即对应的输出项在输出设备上所占的字符数。N指精度。用于说明输出的实型数的小数位数。对数值型的来说,未指定n时,隐含的精度为n=6位。
  ⑤l或h:l 对整型指long型,对实型指double型。h用于将整型的格式字符修正为short型。
格式字符:d:整型;s:字符型;c:字符型;

8.构造函数,拷贝构造函数,析构函数,拷贝赋值运算

拷贝构造函数:类对象之间的复制,c (const &c C){}.
以下情况都会调用拷贝构造函数:
一个对象以值传递的方式传入函数体 (函数形参和实参)
一个对象以值传递的方式从函数返回 (return 值,而不是引用)
一个对象需要通过另外一个对象进行初始化。
用花括号列表初始化一个数组中的元素或一个聚合类中的成员。(聚合类是指没有用户定义的构造函数,没有私有和保护的非静态数据成员,没有基类,没有虚函数)。
拷贝赋值运算: 与类控制初始化一样,类也可以控制对象如何赋值。重载赋值运算符有如下情况:

  • 重载运算符本质上是函数,其名字由operator关键字后接表示要定义的运算符的符号组成。因此,赋值运算符就是一个名为operator=的函数。类似于任何其他函数,
    运算符函数也有一个返回类型和一个参数列表。
  • 如果是一个运算符是一个成员函数,其左侧运算对象就绑定到隐式的this参数。对于一个二元运算符,例如赋值运算符,其右侧运算对象作为显式参数传递。
  • 拷贝赋值运算符接受一个与其类相同类型的参数:
    cpp class Foo{ public: Foo& operator=(const Foo&); //赋值运算符 //… };
    为了与内置类型的赋值保持一致,赋值运算符通常返回一个指向其左侧运算对象的引用。注意,标准库通常要求保存在容器中的类型要有其赋值运算符,且其返回值是左侧运算对象的引用。
    析构函数:析构函是类的一个成员函数,名字由波浪号接类名构成。它没有返回值,也不接受参数
    在一个构造函数中,成员的初始化时在函数体执行之前完成的,且按照它们在类中出现的顺序进行初始化。在一个析构函数中,首先执行函数体,然后销毁成员。成员按初始化顺序的逆序进行销毁。
    无论何时一个对象被销毁,就会自动调用其析构函数:
    变量在离开其作用域时被销毁;
    当一个对象被销毁时,其成员被销毁;
    容器(无论是标准容器还是数组)被销毁时,其元素被销毁;
    对于动态分配的对象,当对指向它的指针应用delete运算符时被销毁;
    对于临时对象,当创建它的完整表达式结束时被销毁 ;
    对于派生类的构造函数,定义对象时构造函数的执行顺序为:基类的构造函数;成员对象的构造函数;派生类的构造函数。

9.c++内存分配管理

i).栈区

栈是一种后进先出的数据结构。栈区与栈的操作方式类似。由编译器在需要的时候自动分配,不需要时释放,一般存放函数的参数值,局部变量的值。栈可以动态扩展和收缩

ii).堆区(heep)

数据结构的堆是一种经过排序的树形数据结构(一般指二叉堆),与我们要说的堆区没有联系,堆区的分配方式类似于链表。由程序员分配释放(由new分配的内存块,要用delete去释放),若程序员不释放,程序结束时由os回收。堆可以动态扩展和收缩
注:由malloc等分配的内存块,由free释放的,和堆十分相似,我们可以叫做自由存储区(不严格区分的话也是堆区)。

iii).全局区(静态区,static)

全局变量和静态变量存放在一起,初始化的全局变量和静态变量在一块区域,未初始化的则在相邻的另一块。程序结束由系统释放。

iv)文字常量区

常量字符放在这里,程序结束有系统释放

v)程序代码区

存放函数体的二进制代码。

32位编译器:
char :1个字节
char*(即指针变量): 4个字节(32位的寻址空间是2^32, 即32个bit,也就是4个字节。同理64位编译器)
short int : 2个字节
int: 4个字节
unsigned int : 4个字节
float: 4个字节
double: 8个字节
long: 4个字节
long long: 8个字节
unsigned long: 4个字节

64位编译器:
char :1个字节
char*(即指针变量): 8个字节
short int : 2个字节
int: 4个字节
unsigned int : 4个字节
float: 4个字节
double: 8个字节
long: 8个字节
long long: 8个字节
unsigned long: 8个字节

一个class对象占用内存空间大小:
*非静态成员变量总合。
*加上编译器为了CPU计算,作出的数据对齐处理。
*加上为了支持虚函数,产生的额外负担。
空类的大小为1;函数不占用类的空间;虚函数占用额外空间(4,正是虚函数指针vptr的大小)

猜你喜欢

转载自blog.csdn.net/m0_37687861/article/details/81878484