c++基础知识总结

一.const的用法

1.用于指针
const int *p:这就代表值不能够更改(*p,*p的值)
int *const p:不能更改指向的地址,也就是说不能指向别的地方
const int *const p:既不能更改地址,也不能更改变量的值
2.const用于函数的地址传递参数,修饰函数的参数
void foo(const int *p);
这种形式通常用于在数组形式的参数中模拟传值调用。
也就是相当于函数调用者声称:“我给你一个指向它的指针,但你不能去修改它。”
如果函数编写者遵循了这个约定,那么就相当于模拟了值传递。
这也是const最有用之处了:用来限定函数的形参,这样该函数将不会修改实参指针所指的数据。
这里注意了,是函数不应该去修改而不是不能修改,也就是说const不能阻止参数的修改。
3.const返回函数类型
例如:const int foo();const char *get();
此时就和const的中文表达意义一样,返回的是常量,也就是说是返回之后不可以更改的数值或者指针。
4.const的注意要点(其中类定义出来的成员叫做对象)
a. const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数。
b. const对象的成员是不可修改的,然而const对象通过指针维护的对象却是可以修改的。(这里的转换是说,我另建立了一个指针,而不是原来的东西,简单的来说就是一个copy,去掉了const属性(当然真实情况下并不是真正的copy)意思就是转换成了一个非const成员)
c. const成员函数不可以修改对象的数据,不管对象是否具有const性质。它在编译时,以是否修改成员数据为依据,进行检查。
d. 然而加上mutable修饰符的数据成员,对于任何情况下通过任何手段都可修改,自然此时的const成员函数是可以修改它的。

二.引用的用法

1.引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。但引用必须初始化(int a=10;int &ra =a)。
2.应用可以作为函数的参数void foo(int &p1,int &p2)。不调用拷贝构造,节省时间和效率。
3.常引用,不能通过引用来改变变量的值,但是可以通过变量自己来改变引用的值。引用型参数应该在能被定义为const的情况下,尽量定义为const 。
4.引用作为返回值。能够不产生被返回值的副本。
解释(函数返回值有副本机制,返回之后,另外在保存一份在寄存器)
随之而来的也有规矩:
a.返回值不能作局部变量,必须为全局变量
b.不能返回函数内部new分配的内存的引用。
c.可以返回类成员的引用,但最好是const。
5.引用是除指针外另一个可以产生多态效果的手段。这意味着,一个基类的引用可以指向它的派生类实例。

三.#define的用法

define的优点显而易见,现在来说下define的缺点:
a.相对不是很安全,是在编译前就将其全部替换,不能检查类型
b.未加括号容易带来边界效应,就是的不到想要的结果,解决方法就是给每个变量值都加上括号
1.#define的变体,即#ifndef,可以防止头文件的重复引用(编写头文件的时候使用)
2.#define的变体,即#ifdef,可以实现加入自己需要的模块(源文件)
3.#define max(x,y) (x)>(y)?(x):(y);可以实现函数定义
4.#define既可以定义单行的代码也可以定义多行代码,关键是要在每一个换行的时候加上一个”/”
5.可以通过#define设置编译环境
#ifdef WINDOWS ...... ...... #endif #ifdef LINUX ...... ...... #endif
6.undef可以取消宏
7.条件编译

四.构造函数和析构函数

构造函数是一种特殊的成员函数,与其他函数不同,不需要用户调用它,而是创建对象的时候自动调用。析构函数是对象不再使用的时候,需要清理资源的时候调用。
A.构造函数
1.构造函数的的名称与类名相同
2.构造函数没有返回类型
3.可以在定义的时候有参数
4.一般情况下,编译器会自动调用构造函数,特殊情况下会手动调用。
无参构造,有参构造和拷贝构造
这里只介绍拷贝构造

class Test
{
   private:
          int x;
   public:
          Test(const Test& a)
          {
             this->x=a.x;
          }
}

 Test a(10);
 Test b(a);//调用的为拷贝构造
 Test b=a;//此时调用的也为拷贝构造

拷贝构造被调用的三种情况
a.当用一个对象去初始化同类的另一个对象时
b.如果某函数有一个参数是类A的对象,那么该函数被调用时,类A的赋值构造函数将被调用(假如用引用会调用么???,应该不会)
c.如果函数的返回值是类A的对象时,则函数返回时,A的拷贝构造函数将被调用(产生副本)
析构函数
1.析构函数可以写在类里面也可以不写,编译器会自动调用,名称为类名前加~
2.析构函数没有返回类型和参数

五.New,delete和malloc,free的区别

malloc和free是c的标准库函数 需要头文件支持
new和delete是c++的操作运算符 需要编译器支持(代表可以重载)
1.malloc需要自己情趣分配的内存大小,new是编译器计算出需要的内存大小
2.new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。
3.new内存分配失败时,会抛出bac_alloc异常。malloc分配内存失败时返回NULL。
4.new可以为对象分配内存,malloc不可以
5.new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存

六.访问限定符

无继承关系
1.public
可以被类里面的成员函数,友元类,友元函数访问,是可公开的。
2.protected
相比于public,少了一个被成员运算符访问的权限
3.private
只能被该类的友元函数和方法访问
有继承关系
在这里插入图片描述

七.深拷贝和浅拷贝

浅拷贝
在这里插入图片描述
b的改变其实会影响a的改变,同时a原本指向的空间发生泄漏。
简单来说就是只复制了一个地址。可能造成崩溃或者内存泄漏。
深拷贝
每个对象共同拥有自己的资源,必须显式提供拷贝构造函数和赋值运算符。
拷贝时开辟一个新的空间,再赋值。

八.友元函数

定义:
友元函数是指定义在类外的函数,通过加上friend从而能够访问类中的私有成员。友元函数不管是在私有部分定义,还是在公有定义都是一样的效果。
友元类
friend class 类名;
类名必须是一个已经定义过的类

class A{
public:
	friend class B;
}

友元关系不能够被继承,友元关系是单向的,且不具有传递性。
类似于男女朋友。

九.static

局部静态变量
首次申明就会被初始化的静态变量,不会随着函数的实现而销毁,会保存在内存的全局数据区,不会随着函数的调用而初始化多次,只会在第一次调用时初始化。相比于全局变量的优点,可控制,变量属于函数本身。(假如没有被显式初始化,会被缺省初始化为0);
静态全局变量
静态全局变量在同一个文件中,与全局变量的使用相同。但与全局变量也有区别,静态全局变量有文件隔离,相当于只能在定义此静态全局变量的文件中使用,不能在其他文件中使用,全局变量可以。
扩充:
静态函数与静态全局变量的性质类似,就不再详细赘述。
静态数据成员
对于静态数据成员,每个类只有一个,它不占用类的内存,而是在静态区开辟一个内存,一个类中的静态变量每个类对象都共享,静态数据成员不能在类的声明里初始化。
静态成员函数
可以使用类名::函数名使用,也可以使用对象名.函数名使用;
静态成员之间可以相互访问,非静态成员可以访问静态成员,静态成员不能访问非静态成员。因为静态是属于类的,他不知道你的对象里面的成员到底是什么,值是多少,而对象对类清清楚楚的知道里面有什么。

十.内联函数

执行函数虽然简便,但会花费更多的时间,内联函数,是将函数框里的整个代码段插入主函数,不会造成时间的浪费,但会消耗空间,碰见小的简便函数,建议用内联函数。函数前面加inline。

十一.继承与虚继承

子类对象可以赋值给父类对象,父类对象不能赋值给子类。
父类指针可以指向子类对象,子类对象不能指向父类。
子类和父类有同名成员,子类将隐藏父类对成员的访问,想要调用同名函数,可以直接使用基类::函数名使用。
函数重载,作用域相同,函数名相同,函数的参数不同,与函数的返回值没有关系。
函数隐藏,作用域不同,例如子类和父类,函数的参数相不相同无关,返回值也无关。派生类对象调用同名的函数,派生类优先调用属于自己的函数。
虚继承:
钻石继承问题衍生出来的问题就是孙子得到两个父类的继承,从而产生二义性的问题,还会产生数据冗余。
在这里插入图片描述

//类似于这样的代码就不会产生二义性
class person{}
class p1:virtual public person;
class p2:virtual public person;
class s:public p1,public p2;
//其中virtual就是虚继承

这样继承s就会只继承p2的共有数据,不会产生二义性。

十二.钻石继承问题

具体问题见十一。
解决原理:
在虚继承的时候,继承的类不光有自己的东西,还有一个虚指针,虚指针指向一个虚基表,可以看作是一个数组,这个表里存的是父类里面共有的数据的相对偏移量,通过这个偏移量就可以找到相对的成员,解决了二义性问题。

十三.同名覆盖问题

当父类指针或者引用指向子类时,子类会退化为父类,只能使用父类存在的成员,可以直接访问被子类覆盖的同名成员。

十四.重载.隐藏和覆盖/重写

这是三个特别容易引起混淆的概念
1.重载
要求:
a.函数名称相同
b.参数列表不同
c.返回类型随意
d.同一个作用域(同一个类)
e.virtual关键字可有可无
匹配规则:
严格匹配,找到就调用,没找到通过隐式转化,找到合适的重载函数。
例子:

class exp{
public:
   void myfun(int a);
   void myfun(char c);
}

2.隐藏
隐藏规则的底层原因:
是因为c++的名字解析过程,派生类的类域被嵌套在基类的类域中。首先在派生类类域中查找该名字,如果无法找到,则在基类类域中查找。
规则:
a.不同的作用域,并且存在继承关系。
b.不论基类函数是否为virtual,只要有派生类函数同名不同参,该基类函数在派生类中隐藏。
c.基类函数非virtual,有派生类函数与此基类函数同名同参,基类函数同样被隐藏。
如何访问被隐藏的成员:
1.用using关键字
using 基类::函数名
这样可以在派生类不隐藏,重载该函数。
2.用域限定符::直接调用
派生类.基类::函数名();
develop.base::fun();
就可调用
3.覆盖,重写(覆盖发生在虚表中,看似无,实际也无)
要求:
a.不同作用域,存在继承关系。
b.函数名参数都相同
c.基类函数有virtual关键字
d.相同的返回类型
特点:
与访问级别无关,就算是private也可以重写。
const可能会使虚函数的重写失效
访问限定符可以不同

十五.多态

在这里插入图片描述
函数重载前面说过,什么是泛型编程?
就是使用stl之内的进行编程。
动态多态:
要有继承,虚函数重写,父类指针指向子类对象(引用也可)。
总结一道面试题:那些函数不能定义为虚函数?
经检验下面的几个函数都不能定义为虚函数:
1)友元函数,它不是类的成员函数
2)全局函数
3)静态成员函数,它没有this指针
2和3都在全局区里存储
4)构造函数,拷贝构造函数,以及赋值运算符重载(可以但是一般不建议作为虚函数)
在这里不得不提下接口:
a.所有成员都是公有的
b.没有定义任何的成员变量
c.是一种特殊的抽象类
抽象类:
与其他类不同的就是不能实例化。
派生类必须实现未实现的方法。
成员必须有纯虚函数。

virtual intchar *c)=0;

虚函数表的解析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值