C++基础知识点

1.什么是多态
同一操作作用于不同的对象时,可以有不同的解释,产生不同的执行结果。
一般分为运行时的多态和编译时的多态
编译时的多态:函数重载,根据传递的参数,返回的类型决定实现何种操作
运行时的多态:知道系统运行时,才根据实际情况实现何种操作,一般通过虚函数实现


2.虚函数怎么实现的
C++中的虚函数的作用主要是实现了多态的机制,虚函数(Virtual Function)是通过一张
虚函数表(Virtual Table)来实现的通过对象实例的地址得到这张虚函数表,然后就可
以遍历其中函数指针,并调用相应的函数.


3.在基类构造函数还没完成之前,虚拟机制尚未开始(参考C++面试秘籍中的面试题10)


4.四种类型转换
4.1.dynamic_cast <new_type> (expression) 
用于class的指针或者引用之间的类型转换:例如派生类指针转换为基类

https://blog.csdn.net/lianghui0811/article/details/76487416?locationNum=8&fps=1
4.2.reinterpret_cast <new_type> (expression)
二进制序列(指针或者其他类型变量的内存表示形式)的重新解释,比如将具有指针
意义的二进制序列解释为具有整数意义的二进制序列
4.3.static_cast <new_type> (expression)
所有其他的隐式类型转换 :void指针转换为其他的指针,int double类型转换为枚举类型 
4.4 const_cast <new_type> (expression)
const变量转换成非const型的变量;或者将非const变量转换成const变量


5.操作符重载:
操作符重载就是把操作符(比如'+,-,*,/'这些运算符)赋于新的意义


6.内存对齐的原则:
1).数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,
   以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如
   说是数组,结构体等)的整数倍开始(比如int在32位机为4字节, 则要从4的整数倍地址开始存储),
   基本类型不包括struct/class/uinon。


2).结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部"最宽基本类型成员"的整
   数倍地址开始存储.(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储.)。


3).收尾工作:结构体的总大小,也就是sizeof的结果,.必须是其内部最大成员的"最宽基本类型成员"的整数倍.不足
   的要补齐.(基本类型不包括struct/class/uinon)。


4).sizeof(union),以结构里面size最大元素为union的size,因为在某一时刻,union只有一个成员真正存储于该地址。


7.模板:分为类模板和函数模板
C++为我们提供了函数模板机制。所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,
用一个虚拟的类型来代表。这个通用函数就称为函数模板,先声明一个关键字template告诉C++编译器 我要开始泛型了.你不要随便报错


8.指针和const的用法
指针常量:是一个常量,指针指向的内容可以改变,本身是常量不能改变
常量指针:是一个指针,指向的内容不可以改变,本身可以修改
const 在*左边,修饰指针,表示常量指针
const 在*右边  修饰变量,指针常量


实例:
 int a=3;
 int b;  
/*定义指向const的指针(指针指向的内容不能被修改)*/ 
 const int* p1; 
 int const* p2; 
 /*定义const指针(由于指针本身的值不能改变所以必须得初始化)*/ 
 int* const p3=&a; 
/*指针本身和它指向的内容都是不能被改变的所以也得初始化*/
 const int* const p4=&a;
 int const* const p5=&b;  
 p1=p2=&a; //正确
 *p1=*p2=8; //不正确(指针指向的内容不能被修改)  
 *p3=5; //正确
 p3=p1; //不正确(指针本身的值不能改变)    
 p4=p5;//不正确 (指针本身和它指向的内容都是不能被改变) 
 *p4=*p5=4; //不正确(指针本身和它指向的内容都是不能被改变) 


9.内联函数:解决程序调用中的效率问题,函数的代码放于符号表中,没有调用的开销
  宏定义只是预处理的符号表中的简单替换,没有存在编译器的类型检查


10.内联函数和宏定义区别:
   宏定义不是函数,内联函数是函数
   宏定义预编译是展开,内联函数编译时展开
   内联函数会进行类型检查等编译功能
   宏定义容易出现二义性


11.const和#define

const常量存在于程序的数据段,#define常量存在于程序的代码段

#define PI 3.1415926 当程序编译的时候,编译器首先会将之后的所有代码的PI全部换成3.1415926,然后进行编译

因此它的生命周期止于编译器,它存在于程序的代码段,在实际程序中它只是一个常数。

const常量存在于程序的数据段,并在堆栈分配了空间,const常量是一个Run-time的概念,它在程序中确确实实可以被调用

传递。const常量有数据类型,而宏常量没有数据类型,在effctive c++条款二中就有这么一段话:

①#define LENSIZE 20

在预处理时,编译器会将代码中的所有LENSIZE简单的替换为20,因此宏常会出现一些难以预料的问题。按书中所述:LENSIZE也许从未被编译器看见,记号名称LENSIZE有可能未进入符号表(symbol table)内。当出现异常报错时,错误信息可能是20而非LENSIZE,如果LENSIZE定义在某个头文件中,这样对于错误的追踪便会相对麻烦。并且宏不重视作用域,不能提供任何封装性

②宏定义函数展开的的问题

#define MAX(a,b) a > b ? a : b  
int main()  
{  
   int a = 8;  
   int b = 6;  
   MAX(a,b);  
   return 0  

}  

实际效果:

int main  
{  
   MAX(++a,b);  
   //将宏替换过来便能发现问题  
   //++a > b ? ++a : b  
   //显然变量++a操作进行了两次  

   return 0;  

}

总结:对于单纯常量,最好以const对象或enums替换#define

          对于形似函数的宏,最好用inline函数替换#define(两者区别可以参考第10点)

const有什么作用:
1)定义常量
2)修饰函数形式参数
3)修饰函数的返回值
 

effective c++条款3中的介绍:

1. 修饰变量,使之不可改变
const int var = 3;
此时var的值就不能改变了。也正是因为const的变量不能轻易修改存储的值,所以在声明的时候就要初始化,这样就是不行的:
const int var;

编译器就会报错。

2. 修饰指针
指针是特殊的变量,有时我们希望对它所指向的对象操作,而有时我们又希望对指针本身进行操作。同样,const应用于指针也有两个含义:一个是指向常量(指向的内容不可更改),一个是常量指针(指针的指向不可更改)。看下面这两个例子:
const int* p = &a;/* p为指向常量的指针,即p指向的对象是常量,不可以通过*p = 3 来修改a的值,但这时p = &b换个指向还是可以的 */
int* const p = &a; /* p为常量指针,即p的指向不可更改,不可以通过p = &b来修改p的指向,但这时*p = 3改变a的值还是可以的 */
const int* const p = &a; /* p为指向常量的常量指针,p的指向以及指向的对象都不可以更改,无论是*p = 3,还是p = &b都是错误的 */
还有一种形式是int const *p,这种形式是表示常量指针,还是指向常量的指针呢?Effective C++给出的建议是看“*”的位置,当const位于星号左侧时,const修饰的是值,即表示指向常量,而当const位于星号右侧时,const修饰的是指针,即表示常量指针。所以int const *p等价于const int *p,你想对了吗?(关键看const修饰的是*p还是p)。
const有时还会修饰函数的形参或者函数的返回值,都是属于1或2这两种情况。修饰函数形参的用法:
void fun(const char a)
{
a = ‘d’; // 错误,因为的值不可以改变
cout << a; // OK
}
还有一个地方要注意一下,若有:
void fun1(const char* a)
{
cout << a << endl;
}
void fun2(char *a)
{
cout << a << endl;
}
当实参为const时,比如const char* msg = “hello”,此时fun1(msg)是可以的,但fun2(msg)会报编译错,说是无法将const char*转成char*;而当实参为普通变量时,比如char* msg = “hello”,fun1(msg)和fun2(msg)都是OK的。这是为什么呢?因为当const的变量传递给非const的变量会不安全(非const的变量可以修改原来定义为常量的东西了!),所以C++限制了这种用法(需用强制类型转换来告诉编译器,编译器才会放行);而反过来,当非const的变量传递给const变量时,不存在安全问题,所以C++编译器总是会放行的。因此,如果在函数体内确实不改变形参a的值,那么采用带const的fun1的写法会更好,适用性更强。
3. 修饰迭代器
C++的STL中使用迭代器作为接口,它定义了普通的迭代器,如vector<T>::iterator,也定义了指向常量的迭代器,如vector<T>::const_iterator,初学者可能想当然地认为const vector<T>::iterator等价于vector<T>::const_iterator,其实不是这样的,const vector<T>::iterator表示这个迭代器的指向不可以更改,即表示的是常量迭代器。
4. 在类中修饰成员函数
const放在类中成员函数的后面,表示这个成为函数不会修改类的成员变量,比如:
class A
{
private:
int a;
double b;
public:
void fun1() const;
void fun2();
};
注意这里的fun1()函数后面有一个const,表示这个函数不会修改类的成员变量(在它的函数体里面出现任何改变a或b的值的操作,均不能通过编译);另一方面fun2()函数后面没有const,表示这个函数可能修改类的成员变量,注意这里用的词是“可能”,fun2()可以修改也可以不修改,但为了增强安全性,所以良好的编程风格一般会把不改动成员变量的成员函数修饰为const的。
有一点要切记:有无const是可以构成成员函数的重载的!
在本书中还提到了一个尖锐的问题,如果假定类是这样的:
class B
{
private:
int* p;
public:

};
我们看到,类的成员函数是指针,假定它在构造函数时会被初始化,而指向一段内存空间。那么如果不改变p本身(即指向不变),但是改变了p指向的内容(比如*p = 3),这样到底算不算对成员变量进行改动了呢?
读者可以在VS环境中写一下测试用例,可以发现VS编译器对这种情况是放行的,*p = 3完全可以通过,但是p = &b就不可以了。
虽然编译器是放过你了,但这也许并不是你的本意,本书中推荐的是“从逻辑上看”,就要交由写代码的你去好好思量一下。如果在某个函数里确实改动了p所指向的内容,那么最好就不要加上const;反过来,如果加上了const就不要改变成员变量,包括它所指向的值。
在const和非const成员函数中避免重复
我觉得这是一个非常重要的内容,有没有加const是构成函数重载的,但通常这种重载的相似度很高,就用书上的例子:
class TestBlock
{
private:
string text;
public:
...
const char& operator[](size_t position) const
{

return text[position];
}
 
char& operator[](size_t position)
{

return text[position];
}
};
可以看到两个重载函数里面的操作都是一样的,别因此认为可以用ctrl+c,ctrl+v而省事了,如果你要改动其中一个函数体里的内容,另一个就要同步更新,而万一你忘记了更新,后果是非常严重的!
一个好的方法来实现同步——在非const的函数中调用const函数!这样来修改:
char& operator[] (size_t position)
{
return const_cast<char&>(
static_cast<const TestBlock&>(*this)[postion]
);
}

12.static的作用
1)在函数体内,一个被声明为static的变量在这一函数调用的过程中维持其值不变
2)在模块内,函数体外,声明为static的变量可以未整个模块所有函数所用
3)在模块内,被声明为static的函数只能在该模块内使用


13.extern C的作用
extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern "C"
后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。由于C++支持函数重载,
因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函
数名;而C语言并不支持函数重载,因此编译C代码的函数时不会带上函数的参数类型,一般
之包括函数名


extern 表明该变量在别的地方已经定义过了,在这里要使用那个变量
函数的声明中带有关键字extern,仅仅是暗示这个函数可能在别的源文件里定义


14.C与C++的区别
解决问题的思想方法不同,C是面向过程的,C++扩展了C的内容,有是面向对象的内容


15.常见的排序算法:
冒泡法,选择法,插入排序,快速排序,堆排序,归并排序,希尔排序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值