【CS基础】C++汇总


0.指针

link
指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:

type *var-name;

在这里,type 是指针的基类型,它必须是一个有效的 C++ 数据类型,var-name 是指针变量的名称。用来声明指针的星号 * 与乘法中使用的星号是相同的。但是,在这个语句中,星号是用来指定一个变量是指针。以下是有效的指针声明:

int    *ip;    /* 一个整型的指针 */
double *dp;    /* 一个 double 型的指针 */
float  *fp;    /* 一个浮点型的指针 */
char   *ch;    /* 一个字符型的指针 */

实际用法

int  var = 20;   // 实际变量的声明
   int  *ip;        // 指针变量的声明
 
   ip = &var;       // 在指针变量中存储 var 的地址
 
   cout << "Value of var variable: ";
   cout << var << endl;
 
   // 输出在指针变量中存储的地址
   cout << "Address stored in ip variable: ";
   cout << ip << endl;
 
   // 访问指针中地址的值
   cout << "Value of *ip variable: ";
   cout << *ip << endl;
 
   return 0;

区别传递指针给函数以及传递引用给函数
C++ 允许您传递指针给函数,只需要简单地声明函数参数为指针类型即可。

#include <iostream>
#include <ctime> 
using namespace std;
 // 在写函数时应习惯性的先声明函数,然后在定义函数
void getSeconds(unsigned long *par);
int main ()
{
   unsigned long sec;
   getSeconds( &sec ); 
   // 输出实际值
   cout << "Number of seconds :" << sec << endl;
   return 0;
}
void getSeconds(unsigned long *par)
{
   // 获取当前的秒数
   *par = time( NULL );
   return;
}

形参相当于是实参的“别名”,对形参的操作其实就是对实参的操作,在引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址
被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。
引用和指针区别
指针从本质上讲就是存放变量地址的一个变量,在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变。 而引用是一个别名,它在逻辑上不是独立的,它的存在具有依附性,所以引用必须在一开始就被初始化,而且其引用的对象在其整个生命周期中是不能被改变的(自始至终只能依附于同一个变量)

1.字符数组

  • 比如char a[10],char和[10]搭配,意思是10个字符长的字符数组,a就是一个这样的字符数组。

  • 那么char a[10]呢?char是字符指针,char*和[10]搭在一起,意思是10个字符指针长的字符指针数组,a就是这样一个字符指针数组。

  • 而char(*a)[10]呢?因为有括号,*是和a结合在一起,而char是和[10]在一起,则是10个字符长的字符数组,*a是这样的一个字符数组,也就是说a是指向这样一个字符数组的指针。

  • typedef char T[10]呢?char和[10]搭在一起,意思是10个字符长的一个数组,typedef定义了T是char[10]这样的一种数据类型。
    T * a;*应该和a搭在一起,所以a就是指向T这样一种数据类型的指针,所以等价于char (*a)[10]

2. C++ 虚函数和纯虚函数的区别

定义一个函数为虚函数,不代表函数为不被实现的函数。

定义他为虚函数是为了允许用基类的指针来调用子类的这个函数。

定义一个函数为纯虚函数,才代表函数没有被实现。

定义纯虚函数是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数。

2.1 纯虚函数

纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加 =0:

virtual void funtion1()=0
、为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。

2、在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。

为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数(方法:virtual ReturnType Function()= 0;),则编译器要求在派生类中必须予以重写以实现多态性。同时含有纯虚拟函数的类称为抽象类,它不能生成对象。这样就很好地解决了上述两个问题。

声明了纯虚函数的类是一个抽象类。所以,用户不能创建类的实例,只能创建它的派生类的实例。

3.函数模板&类模板

函数模板
类模板

4.Qt面试题

Qt基础面试题

2.25 指针和引用区别

  • 1、两者的定义和性质不同
    指针是一个变量,存储的是一个地址,指向内存的一个存储单元;
    引用是原变量的一个别名,跟原来的变量实质上是同一个东西。
    而引用 r,是 a 的一个别名,在内存中 r 和 a 占有同一个存储单元。

  • 2、指针可以有多级,引用只能是一级

  • 3、指针可以在定义的时候不初始化,引用必须在定义的时候初始化

  • 4、指针可以指向NULL,引用不可以为NULL

  • 5、指针初始化之后可以再改变,引用不可以

  • 6、sizeof 的运算结果不同
    int a = 996;
    int p = &a;
    int &r = a;
    cout << sizeof§; // 返回 int
    类型的大小
    cout << sizeof®; // 返回 int 类型的大小
    在64位机器上,int* 类型的大小为8个字节,int类型的大小为4个字节。

2.26 C++基础知识

2.26.1 sizeof字符数组

char a[5] = {‘h’, ‘e’, ‘l’ , ‘l’, ‘o’};
那么sizeof(a) = 5;
如果char a[] = “hello”;//或者char a[6] = “hello”
那么sizeof(a) = 6;//加上字符串中的最后一个"\0"
sizeof()是获得字符数组在内存中所占的字节数,你的说法也有点说的过去吧.反正这两个数字大小一样.
要获得字符数组的实际长度(不包括\0),就用strlen();

2.26.2 c=a+++b

inta=5,b=7,c;c=a+++b; c=(a++)+b=12;

2.26.3 在C++程序中调用被C编译器编译后的函数,为什么要加extern"C"

C++语言支持函数重载,C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不同。假设某个函数的原型为: void foo(int x, int y);该函数被C编译器编译后在库中的名字为foo,而C++编译器则会产生像foointint之类的名字。C++提供了C连接交换指定符号extern"C"来解决名字匹配问题。
参考链接link
参考链接2link

2.26.4 栈和堆的区别

1、栈由系统自动分配,而堆是人为申请开辟;2、栈获得的空间较小,而堆获得的空间较大;3、栈由系统自动分配,速度较快,而堆一般速度比较慢;4、栈是连续的空间,而堆是不连续的空间。”

2.27.5 什么是预编译:

预编译又称为预处理 , 是做些代码文本的替换工作。
处理以# 开头的指令 , 比如拷贝 #include 包含的文件代码,#define 宏定义的替换 , 条件编译等,就是为编译做的预备工作的阶段。

2.27.6 char * const p char const * p const char *p 上述三个有什么区别?

char * const p; //常量指针,p的值不可以修改
char const * p;//指向常量的指针,指向的常量值不可以改
const char *p; //和char const *p
参考

  • const和define区别
    (1)就起作用的阶段而言: #define是在编译的预处理阶段起作用,而const是在 编译、运行的时候起作用。
    (2)就起作用的方式而言: #define只是简单的字符串替换,没有类型检查。而const有对应的数据类型,是要进行判断的,可以避免一些低级的错误。
    (3)就存储方式而言:#define只是进行展开,有多少地方使用,就替换多少次,它定义的宏常量在内存中有若干个备份;const定义的只读变量在程序运行过程中只有一份备份。
    (4)从代码调试的方便程度而言: const常量可以进行调试的,define是不能进行调试的,因为在预编译阶段就已经替换掉了。

2.27.7 计算机内部如何存储负数和浮点数?

负数比较容易,就是通过一个标志位和补码来表示。
【计算机中所有数都是以补码形式存储的】

对于浮点类型的数据采用单精度类型(float)和双精度类型(double)来存储,float数据占用32bit,double数据占用64bit,我们在声明一个变量float f= 2.25f的时候,是如何分配内存的呢?如果胡乱分配,那世界岂不是乱套了么,其实不论是float还是double在存储方式上都是遵从IEEE的规范的,float遵从的是IEEE R32.24 ,而double 遵从的是R64.53。更多可以参考浮点数表示。
无论是单精度还是双精度在存储中都分为三个部分:

1). 符号位(Sign) : 0代表正,1代表为负
2). 指数位(Exponent):用于存储科学计数法中的指数数据,并且采用移位存储
3). 尾数部分(Mantissa):尾数部分
其中float的存储方式如下图所示:
在这里插入图片描述

而双精度的存储方式如下图:

在这里插入图片描述

2.27.8 static 有什么用途

1). 静态(局部/全局)变量
2). 静态函数
3). 类的静态数据成员
4). 类的静态成员函数
参考链接
参考链接2

2.27.9 类的静态成员变量和静态成员函数各有哪些特性?

静态成员变量
1). 静态成员变量需要在类内声明(加static),在类外初始化(不能加static),如下例所示;
2). 静态成员变量在类外单独分配存储空间,位于全局数据区,因此静态成员变量的生命周期不依赖于类的某个对象,而是所有类的对象共享静态成员变量;
3). 可以通过对象名直接访问公有静态成员变量;
4). 可以通过类名直接调用公有静态成员变量,即不需要通过对象,这一点是普通成员变量所不具备的。
class example{
public:
static int m_int; //static成员变量
};

int example::m_int = 0; //没有static

cout<<example::m_int; //可以直接通过类名调用静态成员变量
1
2
3
4
5
6
7
8
静态成员函数
1). 静态成员函数是类所共享的;
2). 静态成员函数可以访问静态成员变量,但是不能直接访问普通成员变量(需要通过对象来访问);需要注意的是普通成员函数既可以访问普通成员变量,也可以访问静态成员变量;
3). 可以通过对象名直接访问公有静态成员函数;
4). 可以通过类名直接调用公有静态成员函数,即不需要通过对象,这一点是普通成员函数所不具备的。
class example{
private:
static int m_int_s; //static成员变量
int m_int;
static int getI() //静态成员函数在普通成员函数前加static即可
{
return m_int_s; //如果返回m_int则报错,但是可以return d.m_int是合法的
}
};

cout<<example::getI(); //可以直接通过类名调用静态成员变量

2.27.10 构造函数和析构函数的执行顺序?

构造函数
1). 首先调用父类的构造函数;
2). 调用成员变量的构造函数;
3). 调用类自身的构造函数

2.27.11 构造函数可否为虚函数

构造函数不能声明为虚函数,析构函数可以声明为虚函数,而且有时是必须声明为虚函数。不建议在构造函数和析构函数里面调用虚函数。构造函数不能声明为虚函数的原因是:1 构造一个对象的时候,必须知道对象的实际类型,而虚函数行为是在运行期间确定实际类型的。而在构造一个对象时,由于对象还未构造成功…”

2.28 内存的分配方式的分配方式有几种?

【参考答案】:

1)从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量。

2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

3)从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
【new-delete-malloc-free】

  1. new/delete会调用对象的构造/析构函数, 而malloc/free只会释放内存;
  2. malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符;
  3. 它们都可用于申请动态内存和释放内存;
  4. 对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数;
  5. new操作符申请内存分配时无须指定内存块的大小, 编译器会根据类型信息自行计算;
    而malloc则需要显式地指出所需内存的尺寸

30.条件编译

三、条件编译中使用的预编译指令#
#define 定义一个预处理宏
#undef 取消宏的定义

#if 编译预处理中的条件命令,相当于C语法中的if语句
#ifdef 判断某个宏是否被定义,若已定义,执行随后的语句
#ifndef 与#ifdef相反,判断某个宏是否未被定义
#elif 若#if, #ifdef, #ifndef或前面的#elif条件不满足,则执行#elif之后的语句,相当于C语法中的else-if
#else 与#if, #ifdef, #ifndef对应, 若这些条件不满足,则执行#else之后的语句,相当于C语法中的else
#endif #if, #ifdef, #ifndef这些条件命令的结束标志.
defined  与#if, #elif配合使用,判断某个宏是否被定义

#if 条件语句
程序段1  //如果条件语句成立,那么就编译程序段1  
#endif
程序段2//如果条件不语句成立,那么就编译程序段2 

#ifndef x//先测试x是否被宏定义过  
#define   程序段1 //如果x没有被宏定义过,那么就编译程序段1   
#endif  
程序段2 //如果x已经定义过了则编译程序段2的语句,“忽视”程序段1。 

31.构造函数

link
ans

31.1 浅拷贝” 、“深拷贝

浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
在这里插入图片描述

32 指针数组和数组指针

link

  • 指针数组:指针数组可以说成是”指针的数组”,首先这个变量是一个数组,其次,”指针”修饰这个数组,意思是说这个数组的所有元素都是指针类型,在32位系统中,指针占四个字节。
  • 数组指针:数组指针可以说成是”数组的指针”,首先这个变量是一个指针,其次,”数组”修饰这个指针,意思是说这个指针存放着一个数组的首地址,或者说这个指针指向一个数组的首地址。

根据上面的解释,可以了解到指针数组和数组指针的区别,因为二者根本就是种类型的变量。

33 运算符重载

  • 函数名是由关键字 operator 和其后要重载的运算符符号构成的。
  • 若定义为非成员函数:
Box operator+(const Box&,const Box&)
  • 若定义为成员函数:
// 重载 + 运算符,用于把两个 Box 对象相加
      Box operator+(const Box& b)
      {
         Box box;
         box.length = this->length + b.length;
         box.breadth = this->breadth + b.breadth;
         box.height = this->height + b.height;
         return box;
      }

main函数:

// 把两个对象相加,得到 Box3
   Box3 = Box1 + Box2

注意!!!!
一个类重载operator[] ()函数时,要重载operator[]两次,一个是const函数,一个是非const函数。const函数的返回值用按值传递,非const函数的返回值用引用。只实现一次重载,在某些情况下是会报错的。

34.二维指针

34.1 二维指针应用:传参

在这里插入图片描述


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值