一、基础问题
1、new<>delete和malloc<>free区别:
这两个表达式都用于申请动态内存和释放动态内存。但是new可以用于非内部数据类型的对象,而malloc是无法做到的,因为new和delete是作用于构造函数和析构函数的,是c++的运算符;但是malloc是库函数。
注意delete是调用一次析构函数,而delete[]会对每一个成员都调用析构函数,所以在用new申请了一个动态数组后,要用delete[]进行释放。
注:
c++有三种内存管理方式:自动存储、静态存储、动态存储、(c++11有了线程存储)。
常规变量存储于自动存储中,称为自动变量,通常存储在栈中;
静态存储是整个运行期间都存在的存储方式,有两种方式实现:在函数外面定义、在声明变量时使用关键字static;
动态存储为new和delete提供,它们管理一个内存池,称为自由空间或者堆(Heap),和静态变量、自动变量分开存储,不受函数生存时间控制。所以用完new一定要delete;
2、const与#define 相比,有何优点
const int b =64; 有三个使用场景,主要用于防止以外的变动,与define可以明确常量类型而define只是简单替换:
定义常量
修饰函数参数
修饰函数返回值
3、数组( [] )、引用( & )、指针( * )区别
本质:指针是一个变量,存储内容是一个地址,指向内存的一个存储单元。而引用是原变量的一个别名,实质上和原变量是一个东西,是某块内存的别名。 比如对指针用++就是地址加1,而对引用++就是对值+1;
而对数组而言,数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变。而指针可以随时指向任意类型的内存块,它的特征是“可变”,所以我们常用指针来操作动态内存。最明显的就是用sizeof计算大小的时候。
要保护引用在函数中不被改变可以使用常量引用。
4、指针数组和数组指针的区别
顾名思义,数组指针应该是指向数组的指针,而指针数组则是指该数组的元素均为指针。
数组指针,是指向数组的指针,其本质为指针,形如int (*p)[10],p即为指向数组的指针;数组指针是指向数组首元素的地址的指针,其本质为指针,可以看成是二级指针
指针数组,在C语言和C++中,数组元素全为指针的数组称为指针数组,其中一维指针数组的定义形式为:
类型名 *数组标识符[数组长度]
指针数组中每一个元素均为指针,其本质为数组,例如我们经常使用的动态数组的就是基于此的使用,如下示例:
size_t row,col;
//输入row和col的数值
int **MathTable = new int*[row];
for( int i = 0 ; i < row ; i++ )
MathTable[i] = new int[col];
//code
for( int i = 0 ; i < row ; i++ )
delete [] MathTable[i];
delete []MathTable;
也就是形如int p[10]这样的声明,就是我们这里的指针数组,从声明形态上来讲,是由于[]的优先级高于,又有诸如下面的指针:
*ptr_arry[i]
指针数组中的元素可以表示为:
((ptr_arry+i))
()的优先级较高,又由于又结合的原因,可以化简为:
**(ptr_arry+i)
由于数组元素均为指针,因此prt_array[i]是指第i+1个元素的指针。
二、函数问题
大部分的函数问题都可以归为类的问题里,这里只写一点点
1、返回引用
返回引用的函数可以看成是被引用变量的别名,这样可以让程序运行的更快,最关键的一点是避免返回函数终止时不再存在的内存单元的引用!!错误的例子如下:
const int &fun(int &ft){
int newguy;
newguy = ft;
return newguy;
}
正常有两种方式:
- 返回一个作为参数传给函数的引用
- 用new来分配新的存储空间
2、模板函数
template<class T>
void Swap(T &a, T &b){
T temp;
temp = a;
a = b;
b= temp;
}
二、类初步相关的问题
1、构造函数和析构函数
构造函数用于初始化一些类中的成员变量,析构函数用于释放一些构造函数占用的空间。最常用的是在构造函数中new分配了内存,则在析构函数中要delete这部分空间。如果没有new也可以用默认的构造函数。
2、this指针的作用
在需要对两个对象成员进行比较的时候比如有s1.compare(s2)这个方法,在成员函数中要调用自身的时候就要用到this指针(指向调用对象)
3、作用域为类的常量
我们在定义类的时候想写一个常量给类用的时候希望这么写:
class a1{
private:
const int m1;
int m2[m1]
};
但是这是不对的,因为在声明对象之前没有用于存储值的空间,但是我们可以写为下面的形式:
class a1{
private:
static const int m1;
int m2[m1]
};
因为这个常量和静态常量存储在一起,而不是存储在对象中。
5、static和const进阶作用:
static关键字至少有下列5个作用:
(1)函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
(2)在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
(3)在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;
(4)在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝,也就是派生类和父类中的static是公用空间的;
(5)在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。
const关键字至少有下列5个作用:
(1)欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;
(2)对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;
(3)在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
(4)对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的 成员变量;
(5)对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。
6、友元
C++在控制对类对象中的私有部分访问的时候,除了定义为public,还提供了友元的概念,总共有3种:
友元函数
友元类
友元成员函数
通过让函数变为友元函数可以赋予该函数与类的成员函数相同的访问权限。尤其在重载二元运算符的时候常常用到。 比如在类中
class a1{
friend a1 operator*(double m, const a1 &t);//非成员重载运算符函数
a1 operator*(double m);//成员重载运算符函数
};
在类中,友元并不属于类本身,所以不能继承。
三、类的继承问题
1、什么虚函数?什么是纯虚函数?基类为什么要用虚析构函数?
(1)虚函数就是用于实现多态的,因为一个类函数的调用不是在编译时刻确定的,而是在运行时刻确定的,编写代码的时候并不知道被调用的是基类的函数还是派生类的函数,所以称为‘虚函数’达到多态的效果。核心理念就是通过基类访问派生类定义的函数。
(2)纯虚函数:在基类中声明的虚函数,它在基类中没有定义,但是要求任何派生类都要定义自己的实现方法。
virtual void f1()=0
除了上面虚函数的派生作用外,还因为许多基类对象本身是没有任何意义的,所以干脆变为纯虚函数只用于继承,而且他们必须在继承类中重新声明。
(3)上面说了由于派生的关系并不知道基类的指针调用了哪一个,所以析构函数应当是虚函数,它将调用相应对象类型的析构函数,因此,如果指针指向的是子类对象,将调用子类的析构函数,然后自动调用基类的析构函数。
2、继承时不能继承基类构造函数和析构函数,所以在创建派生类构造函数的时候可以成员初始化表的方式调用基函数的构造函数。但是析构函数是默认调用的
3、私有继承
继承可以分为两大类:is-a和has-a。is-a是指的共有继承,可以将基类的public成员和protected成员原封不动的继承下来,变为派生对象共有接口的一部分(继承接口)。而has-a意味着基类的方法不会成为派生对象接口的一部分,因为他们全变成private成员了(也就是只继承基类的实现)。
protected:当继承是面向派生类而不是面向用户接口中的一部分时,就选用protected继承。
表格如下:
4、虚基类
虚基类使得从多个类(基类相同)派生出的对象只继承一个基类对象。https://blog.csdn.net/woaidapaopao/article/details/77801034