EchoDemo's Blogs

C++中的问题整理(三)

1、define与const的区别

(1)define在预处理阶段进行替换;const在编译时确定其值。

(2)用define可以定义一些简单的函数,const是不可以定义函数的。

(3)用define定义的常量是不可以用指针变量去指向的,用const定义的常量是可以用指针去指向该常量的地址的。

(4)define无类型,不进行类型安全检查,可能会产生意想不到的错误;const有数据类型,编译时会进行类型检查。

(5)define不分配内存,给出的是立即数,有多少次使用就进行多少次替换,在内存中会有多个拷贝,消耗内存大;const在静态存储区中分配空间,在程序运行过程中内存中只有一个拷贝。

(6)宏定义的作用范围仅限于当前文件;而const对象在默认状态下,只在文件内有效,当多个文件中出现了同名的const变量时,等同于在不同文件中分别定义了独立的变量。如果想在多个文件之间共享const对象,必须在变量定义之前添加extern关键字(在声明和定义时都要加。

2、构造函数不能为虚函数,而析构函数可以且常常是虚函数

(1)如果构造函数是虚函数,那么就需要通过vtable来调用,但此时面对一块raw memeory是找不到vtable的,vtable是在构造函数中才初始化的,而不是在其之前。因此构造函数不能为虚函数。

(2)析构函数可以是虚函数,此时vtable已经初始化了,况且我们通常通过基类的指针来销毁对象,如果析构函数不为虚的话,就不能正确识别对象类型,从而不能正确销毁对象。

3、虚函数和纯虚函数的作用与区别

(1)虚函数为了重载和多态的需要,在基类中是有定义的,即便定义是空,所以子类中可以重写也可以不重写基类中的此函数。

(2)纯虚函数在基类中是没有定义的,必须在子类中加以实现,很像java中的接口函数。它不能直接实例化,需要派生类来实现函数定义。

4、面向对象编程关注的是编程的数据方面,而泛型编程关注的是算法。他们之间的共同点是抽象和创建可重用代码,但它们的理念决然不同。泛型编程旨在编写独立于数据类型的代码。在C++中,完成通用程序的工具是模板,模板使得算法独立于存储的数据类型,而迭代器使算法独立于使用的容器类型,它们都是STL通用方法的重要组成部分。

5、头文件中的#ifndef/#define/#endif的作用

其作用是防止该头文件被重复引用。”被重复引用”是指一个头文件在同一个cpp文件中被include了多次,这种错误常常是由于include嵌套造成的。比如:存在a.h文件#include “c.h”而此时b.cpp文件导入了#include”a.h”和#include”c.h”此时就会造成c.h重复引用。有些头文件重复引用只是增加了编译工作的工作量,不会引起太大的问题,仅仅是编译效率低一些。但是对于大工程而言编译效率低下就会是一件很痛苦的事情了。

6、动态内存分配的初始化问题

1)malloc函数:void *malloc(unsigned int size)

在内存的动态分配区域中分配一个长度为size的连续空间,如果分配成功,则返回所分配内存空间的首地址,否则返回NULL,申请的内存不会进行初始化。

2)calloc函数: void *calloc(unsigned int num, unsigned int size)

按照所给的数据个数和数据类型所占字节数,分配一个 num * size 连续的空间。calloc申请内存空间后,会自动初始化内存空间为 0,但是malloc不会进行初始化,其内存空间存储的是一些随机数据。

3)realloc函数:void realloc(void ptr, unsigned int size)

动态分配一个长度为size的内存空间,并把内存空间的首地址赋值给ptr,把ptr内存空间调整为size。申请的内存空间不会进行初始化。

4)new是动态分配内存的运算符,自动计算需要分配的空间,在分配类类型的内存空间时,同时调用类的构造函数,对内存空间进行初始化,即完成类的初始化工作。动态分配内置类型是否自动初始化取决于变量定义的位置,在函数体外定义的变量都初始化为0,在函数体内定义的内置类型变量都不进行初始化。

7、指针和const

int * const x = &y;       //指针所指向的内存不可变,内存中的值可以改变。

const int *x = &y;        //指针所指向的内存可变,但内存中的值不能通过指针改变。

int const *x = &y;        //指针所指向的内存可变,但内存中的值不能通过指针改变。

const int const *x = &y;  //指针所指向的内存不可变,内存中的值也不能通过指针改变。

const int * const x = &y; //指针所指向的内存不可变,内存中的值也不能通过指针改变。

当const在*的左边,则表示指针所指向的空间的内容不能通过改变*p的值来修改,或者说指针所指向空间的值不能被修改。

当const在*的右边,则表示指针的地址不能被修改,或者说指针的指向不能被修改。

8、无论是整型数组还是字符数组,数组名作为右值的时候都代表数组首元素的首地址。数组发生降级(数组名退化为数组首元素的地址)的情况:数组传参、数组名参与运算。数组名不会发生降级的情况:sizeof(数组名)、取地址数组名(取到的是整个数组的地址而不是首元素的地址)。

9、重载、覆盖和隐藏

(1)成员函数被重载的特征:

a、相同的范围(在同一个类中);

b、函数名字相同;

c、参数不同;

d、virtual关键字可有可无。

(2)覆盖是指派生类函数覆盖基类函数,只作用于派生类函数,特征是:

a、不同的范围(分别位于派生类与基类);

b、函数名字相同;

c、参数相同;

d、基类函数必须有virtual关键字。实际上虚函数的作用,就是实现覆盖。

(3)“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:

a、如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。

b、如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。

10、类模板与模板类的概念

(1)类模板:一个类模板(也称为类属类或类生成类)允许用户为类定义一种模式,使得类中的某些数据成员、默认成员函数的参数、某些成员函数的返回值,能够取任意类型(包括系统预定义的和用户自定义的)。如果一个类中数据成员的数据类型不能确定,或者是某个成员函数的参数或返回值的类型不能确定,就必须将此类声明为模板,它的存在不是代表一个具体的、实际的类,而是代表着一类类。

(2)模板类是类模板实例化后的一个产物。可以从类模板派生出新的类,既可以派生类模板,也可以派生非模板类。
类模板的重点是模板,表示的是一个模板,专门用于产生类的模子。模板类的重点是类,表示的是由一个模板生成而来的类。

🐶 您的支持将鼓励我继续创作 🐶
-------------本文结束感谢您的阅读-------------