main函数执行之前,主要就是初始化系统相关资源:
main函数执行之后:
使用指针的情况:
使用引用的情况:
this 是 C++ 中的一个关键字,也是一个 const 指针,它指向当前对象,通过它可以访问当前对象的所有成员。
this指针只能在成员函数中使用,在全局函数、静态成员函数中都不能用this。
static:
类型安全很大程度上可以等价于内存安全,类型安全的代码不会试图访问自己没被授权的内存区域。
C++提供了一些机制保障类型安全:
(1)操作符new返回的指针类型严格与对象匹配。
(2)C++提供了dynamic_cast关键字,使得转换过程更加安全,因为dynamic_cast比static_cast涉及更多具体的类型检查。
重载是指在同一范围定义中的同名成员函数才存在重载关系。主要特点是函数名相同,参数类型和数目有所不同,不能出现参数个数和类型均相同,仅仅依靠返回值不同来区分的函数。重载和函数成员是否是虚函数无关。
重写指的是在派生类中覆盖基类中的同名函数,重写就是重写函数体,要求基类函数必须是虚函数且:
(1)与基类的虚函数有相同的参数个数
(2)与基类的虚函数有相同的参数类型
(3)与基类的虚函数有相同的返回值类型
隐藏指的是某些情况下,派生类中的函数屏蔽了基类中的同名函数,包括以下情况:
(1)两个函数参数相同,但是基类函数不是虚函数。隐藏和重写的区别在于基类函数是否是虚函数。
(2)两个函数参数不同,无论基类函数是不是虚函数,都会被隐藏。和重载的区别在于两个函数不在同一个类中。
以Student类为例,默认构造函数的原型为
Student(); //没有参数
默认构造函数和初始化构造函数在定义类的对象的时候,完成对象的初始化工作。
以Student类为例,初始化构造函数的原型为
Student(int num,int age);//有参数
拷贝构造函数用于拷贝本类的对象,形参是本类对象的引用:
Student s2(1002,1008);
Student s3(s2);//将对象s2复制给s3。注意复制和赋值的概念不同。
//下面这种情况叫做赋值,不调用复制构造函数。
Student s4;
s4=s2;//这种情况叫做赋值
调用场景:
转换构造函数用于将其他类型的变量,隐式转换为本类对象:
Student(int r);//形参时其他类型变量,且只有一个形参
//转换构造函数
Student(int r)
{
int num=1004;
int age= r;
}
移动构造函数主要用对象a自身的空间初始化对象b,避免了新的空间的分配,降低了构造的成本。为了防止浅拷贝导致两个指针共同指向一片内存空间,将第一个指针(比如a->value)置为NULL,这样在调用析构函数的时候,由于有判断是否为NULL的语句,所以析构a的时候并不会回收a->value指向的空间;
浅拷贝只是拷贝一个指针,并没有新开辟一个地址,拷贝的指针和原来的指针指向同一块地址,如果原来的指针所指向的资源释放了,那么再释放浅拷贝的指针的资源就会出现错误。
深拷贝不仅拷贝值,还开辟出一块新的空间用来存放新的值,即使原先的对象被析构掉,释放内存了也不会影响到深拷贝得到的值。在自己实现拷贝赋值的时候,如果有指针变量的话是需要自己实现深拷贝的。
浅拷贝在对象的拷贝创建时存在风险,即被拷贝的对象析构释放资源之后,拷贝对象析构时会再次释放一个已经释放的资源,深拷贝的结果是两个对象之间没有任何关系,各自成员地址不同。
一般我们常说的内存泄漏是指堆内存的泄漏。堆内存是指程序从堆中分配的,大小任意的(内存块的大小可以在程序运行期决定)内存块,使用完后必须显式释放的内存。应用程序般使用malloc,、realloc、 new等函数从堆中分配到块内存,使用完后,程序必须负责相应的调用free或delete释放该内存块,否则,这块内存就不能被再次使用,我们就说这块内存泄漏了。
让某种类型对象获得另一个类型对象的属性和方法。它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
数据和代码捆绑在一起,避免外界干扰和不确定性访问。
封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏,例如:将公共的数据或方法使用public修饰,而不希望被访问的数据或方法采用private修饰。
同一事物表现出不同事物的能力,即向不同对象发送同一消息,不同的对象在接收时会产生不同的行为(重载实现编译时多态,虚函数实现运行时多态)
实现多态有二种方式:覆盖(override),重载(overload)。
覆盖:是指子类重新定义父类的虚函数的做法。
重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。例如:基类是一个抽象对象——人,那教师、运动员也是人,而使用这个抽象对象既可以表示教师、也可以表示运动员。
静态类型转换,对应于C语言中的隐式类型转换场景,可以转换基础数据类型,但是不能转换指针类型。该类型转换会在编译时进行类型检查。
用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换时:
进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;
进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的
重新解释类型,可用于转换指针、引用、算术类型、函数指针或者成员指针。但是不能转换基础数据类型。
动态类型转换,会进行动态类型检查,应用场景是在多态场景中(子类对象传给父类指针或引用),可以动态检查子类的类型。
在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;
在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
应用场景是去除只读属性,但有一个前提是内存本身必须是可以修改的(内存分配在堆栈上)。
它的本质是一个函数,不过它的返回值是一个指针。
# include
# include
int * func_sum(int n)
{
if (n < 0)
{
printf("error:n must be > 0\n");
exit(-1);
}
static int sum = 0; //静态局部变量在整个程序运行期间存在,不加static会错
int *p = ∑
for (int i = 0; i < n; i++)
{
sum += i;
}
return p;
}
int main(void)
{
int num = 0;
printf("please input one number:");
scanf("%d", &num);
int *p = func_sum(num);
printf("sum:%d\n", *p);
return 0;
}
函数指针 的本质是一个指针,该指针的地址指向了一个函数,所以它是指向函数的指针。函数的定义是存在于代码段,因此,每个函数在代码段中,也有着自己的入口地址,函数指针就是指向代码段中函数入口地址的指针。
#include
int max(int a, int b)
{
return a > b ? a : b;
}
int main(void)
{
int (*p)(int, int); //函数指针的定义
//int (*p)(); //函数指针的另一种定义方式,不过不建议使用
//int (*p)(int a, int b); //也可以使用这种方式定义函数指针
p = max; //函数指针初始化
int ret = p(10, 15); //函数指针的调用
//int ret = (*max)(10,15);
//int ret = (*p)(10,15);
//以上两种写法与第一种写法是等价的,不过建议使用第一种方式
printf("max = %d \n", ret);
return 0;
}
回调函数就是一个通过指针函数调用的函数。其将函数指针作为一个参数,传递给另一个函数。回调函数并不是由实现方直接调用,而是在特定的事件或条件发生时由另外一方来调用的。
#include
#include
//函数功能:实现累加求和
int func_sum(int n)
{
int sum = 0;
if (n < 0)
{
printf("n must be > 0\n");
exit(-1);
}
for (int i = 0; i < n; i++)
{
sum += i;
}
return sum;
}
//这个函数是回调函数,其中第二个参数为一个函数指针,通过该函数指针来调用求和函数,并把结果返回给主调函数
int callback(int n, int (*p)(int))
{
return p(n);
}
int main(void)
{
int n = 0;
printf("please input number:");
scanf("%d", &n);
printf("the sum from 0 to %d is %d\n", n, callback(n, func_sum)); //此处直接调用回调函数,而不是直接调用func_sum函数
return 0;
}
C++中的内存分区,分别是堆、栈、自由存储区、全局/静态存储区、常量存储区和代码区。
栈:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
堆:就是那些由 new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个 delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
全局/静态存储区:全局变量、静态变量、常量、字符串常量被分配到同一块内存中,在以前的C语言中,全局变量和静态变量又分为初始化的和未初始化的,在C++里面没有这个区分了,它们共同占用同一块内存区,在该区定义的变量若没有初始化,则会被自动初始化,例如int型变量自动初始为0。
代码区:存放函数体的二进制代码。