一.c++基本概念
1.名字空间:
实际上就是自定义的作用域,目的是尽可能的防止符号命名的冲突
A空间的a,则用A::a表示
using namespace std;引入系统标准名字空间
注意:
a.全局作用域的名字空间是匿名的,引用全局作用域的名字空间只需加::即可
b.名字空间的本质是作用域,遵守c语言作用域的基本原则。如小作用域重名符号会掩盖大作用域的重名符号。
islower:判断是否是小写
toupper:判断是否是大写
2.引用:
给一个变量起一个别名
如: int a=100;
int &b=a; //从此a和b为同一块变量
注意:
a.不可对常量取引用,如 int &a=100;是错误的
b.引用必须在定义的同时赋值,不可单独定义引用,列如: int &a;是错误的
c.引用一方面提高了数据传输的效率,另一方面简化了数据的表达方式
c++标准字符串 :string s
d.在声明处写默认参数
e.默认值只能位于参数列表的最右边
3.函数重载
1.形成函数重载的条件
a.参数个数不同
b.参数类型不同
2.不可以形成函数重载的条件
a.函数名和参数列表完全一致
b.函数返回类型的差异不可以作为函数重载的条件
c.参数是引用的类型,要根据实参的类型。。。
1.实参是常量时不可以
2.实参为变量时可以
3.但一般而言,引用不作为重载的条件
d.静态函数的声明不可以作为函数重载的条件
e.参数时const 类型也要具体分析
1.如果是普通的const类型,则不可以形成重载
2.如果是指针的const类型,则可以形成重载
4.堆类型管理
1.new ,delect跟c语言中malloc(),calloc(),free()的区别
a.申请类类型内存时,new会自动调用类的构造函数;而malloc(),calloc()不会
b.释放类类型内存时,delect会自动调用类的析构函数;而free不会
二.类与对象
1.构造函数
a.没有返回值
b.名字跟类一致
c.构建一个类对象时,会自动被调用
d.主要功能:初始化对象
e.一般是共有权限,不然没法创建对象
f.可以重载,可以有默认参数
2.静态成员
a.代表这些成员不属于某个对象,而属于整个类
b.静态成员数据,在类外初始化
c.静态方法只能访问静态数据
d.静态成员在类外分配内存,与具体对象的的内存管理是分开的,独立的
3.初始化列表
概念:它是一种构造函数的特殊语法,专门给成员初始化
a.const型数据就是只读,初始化之后不能够修改。他只能在初始化列表中赋值,不能在构造函数中赋值
b.普通数据初始列表赋值比构造函数赋值效率高
c.类对象成员初始化,必须在初始化列表中完成
4.析构函数
1.任何一个对象被释放时,会自动调用析构函数
2.析构函数没有返回类型
3.析构函数的名字时固定的,称为:~类名
4.析构函数没有参数,不能重载
5.析构函数的的功能;拆解对象,释放资源,收尾工作
6.一般是公有权限,不然没法释放对象
5.类对象成员
1.一定是先构造完所有对象成员,再构造整体
2.一定是先析构整体,再反析构各个整体
6.类的权限,类域,子类继承方式
1.private:只能在本类域才能访问
2.protected:只能在本类域或者子类域访问
3.public:在任何地方都可以访问
4.子类的继承方式
公有继承:不改变父类的权限限制
三.类的继承
主要解决代码复用的问题
1.如果子类没有显示调用父类的构造函数,那么子类会自动调用父类无参版本的构造函数
2.父类构造函数的显示调用只能出现在初始化列表中
3.怎么指明全局作用域?
答:::a
4.可以指明父类来访问
比如:aima.Car::move();
5.多重继承
可能造成菱形继承,则会调用两次父类的构造函数。可以用虚继承来解决,此时孙子类会直接调用
四.虚函数和多态性
虚函数:
普通虚函数,他有默认行为
1.虚函数的最大机制就是父类可以调用子类实现的接口
2.通过父类的指针或引用,可以访问子类的虚函数版本
3.如果子类没有重写子类a的虚函数版本,那么b->a()就是父类的版本
纯虚函数:
他不提供默认的行为
1.包含纯虚函数的基类称为抽象基类
2.子类必须复写父类的纯虚函数,否则子类也被称为抽象基类
五.深浅拷贝
浅拷贝:
只拷贝了指针,没有复制其真正的内容
深拷贝:
复制了其真正的内容
1.如果类中有指针,那么就意味着有类外资源(内存)存在,需要注意以下几点:
a.需要考虑深浅拷贝问题
b.必须重新析构函数
2.关于this指针
a.任何类方法都有一个默认的隐藏指针,this指针
b.this指针就指向调用该类方法的对象
3.类中默认就有的东西
a.无参构造函数
b.析构函数
c.赋值操作符函数
d.拷贝构造函数
六.运算符重载
对+运算符的重写
1.重载前置自增运算符函数 ++a
注:可以写++a=b;而不能写a++=b;故,前置运算符可以赋值,而后置运算符不能赋值
调用上面的前置,实现后置
七.运算符重载与友元
友元不受权限的限制,因为友元不是类成员
1.友元类
将A类声明为本类的友元,即A类所有的类方法均拥有访问本类所有数据的权限
friend Class A;
2.友元类方法
friend Class A::B;
限定友元类
3.友元函数
friend void A();
4.异常
不同的异常,是由抛出不同对象的类型决定的
八.高级议题
1.如果构造函数只有一个参数,那么初始化的时候可以直接使用参数初始化
2.explicit:使得构造函数必须显示的初始化,不能用同类参数类初始化
3.智能指针
auto_ptr:
a.能在p2退出作用域时,自动释放其对象的作用域
b.普通指针,在没有显示调用delect的情况下,会发生内存泄漏
基本特点:
a.当指针封装在类内部,当临时指针auto_ptr退出时,类A的析构函数被自动调用
b.当多个auto_ptr发生拷贝构造或者赋值时,只有最后一个auto_ptr有效
c.该接口在新的c++标准中已经被废弃
shared_ptr
能在p2退出作用域时,自动释放其对象的作用域
基本特点:
a.当指针封装在类内部,当临时指针shared_ptr退出时,类A的析构函数被自动调用
b.当多个shared_ptr发生拷贝构造或者赋值时,他们共享这个堆内存
unique_ptr
能在p2退出作用域时,自动释放其对象的作用域
基本特点:
a.当指针封装在类内部,当临时指针unique_ptr退出时,类A的析构函数被自动调用
b.不允许多个unique_ptr发生拷贝构造或者赋值时
九.泛型
1.函数模板
如果发现一堆重载的函数只有接口不同,算法的实现基本一致,那么他们适合做成一个统一的模板
函数模板:函数生成器
模板特化:
a.将特化了的类型参数,从模板的类型参数列表剔除,但尖括号<>不能省
b.特化了的函数模板接口必须和通用接口保持一致
c.类型参数列表可以省略
2.类模板
模板代码一律写在头文件中,不能写在cpp中
注意:类模板不是类
十.STL容器
STL:标准模板类
1.常用的容器:
a.线性容器:静态数组【array】,动态数组【vector】,双端队列【deque】,链表【list】
b.容器适配器:栈【stack】,单端队列【queue】,优先级队列【priority_queue】
c.关联型容器:集合【set/multiset】,字典【map/multimap】(键值对,映射表)
d.位集合【bitset】
2.迭代器
就是一个广义的指针,解决了 c++ 中父类 子类对象的指向问题
auto:c++中 代表自动获取数据类型
十一.c和c++ 混合编程
1.一些基本语法问题
char *p;
const char *k;
p=k;将权限放大,是错误的
k=p;将权限缩小,是可以的
异常:
补充:
1.static的作用
全局静态变量:
静态存储区,未经初始化的全局静态变量会被自动初始化为0。
作用域:全局静态变量在声明他的文件之外是不可见的,准确地说是从定义之处开始,到文件结尾。
局部静态变量:
静态存储区,未经初始化的局部静态变量会被自动初始化为0。
作用域:作用域仍为局部作用域,当定义它的函数或者语句块结束的时候,作用域结束。
静态函数:
函数的定义和声明在默认情况下都是extern的,但静态函数只是在声明他的文件当中可见,不能被其他文件所用。
类的静态成员:
静态成员是类的所有对象中共享的成员,而不是某个对象的成员。
类的静态函数:
在静态成员函数的实现中不能直接引用类中说明的非静态成员,可以引用类中说明的静态成员(这点非常重要)。如果静态成员函数中要引用非静态成员时,可通过对象来引用。
2.c++中四种cast转换
a.const_cast
用于将const变量转为非const
b.static_cast
用于各种隐式转换,比如非const转const,void*转指针等, static_cast能用于多态向上转化,如果向下转能成功但是不安全,结果未知;
c.dynamic_cast
只能用于含有虚函数的类,用于类层次间的向上和向下转化。只能转指针或引用。
d.reinterpret_cast
几乎什么都可以转,比如将int转指针,可能会出问题,尽量少用。
3.智能指针
智能指针的作用是管理一个指针,因为存在以下这种情况:申请的空间在函数结束时忘记释放,造成内存泄漏。使用智能指针可以很大程度上的避免这个问题,因为智能指针就是一个类,当超出了类的作用域是,类会自动调用析构函数,析构函数会自动释放资源。所以智能指针的作用原理就是在函数结束时自动释放内存空间,不需要手动释放内存空间。
智能指针主要用于管理在堆上分配的内存,它将普通的指针封装为一个栈对象。当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏。C++ 11中最常用的智能指针类型为shared_ptr,它采用引用计数的方法,记录当前内存资源被多少个智能指针引用。该引用计数的内存在堆上分配。当新增一个时引用计数加1,当过期时引用计数减一。只有引用计数为0时,智能指针才会自动释放引用的内存资源。对shared_ptr进行初始化时不能将一个普通指针直接赋值给智能指针,因为一个是指针,一个是类。可以通过make_shared函数或者通过构造函数传入普通指针。并可以通过get函数获得普通指针。
智能指针有没有内存泄漏的情况?
当两个对象相互使用一个shared_ptr成员变量指向对方,会造成循环引用,使引用计数失效,从而导致内存泄漏。
如何解决?
为了解决循环引用导致的内存泄漏,引入了weak_ptr弱指针,weak_ptr的构造函数不会修改引用计数的值,从而不会对对象的内存进行管理,其类似一个普通指针,但不指向引用计数的共享内存,但是其可以检测到所管理的对象是否已经被释放,从而避免非法访问。
4.数组和指针的区别
5.虚函数与析构函数
a.请问为什么析构函数都是虚函数?而c++默认的析构函数不是虚函数?
将可能会被继承的父类的析构函数设置为虚函数,可以保证当我们new一个子类,然后使用基类指针指向该子类对象,释放基类指针时可以释放掉子类的空间,防止内存泄漏。
C++默认的析构函数不是虚函数是因为虚函数需要额外的虚函数表和虚表指针,占用额外的内存。而对于不会被继承的类来说,其析构函数如果是虚函数,就会浪费内存。因此C++默认的析构函数不是虚函数,而是只有当需要当作父类时,设置为虚函数。
析构函数作用:释放内存。析构顺序:a.派生类本身的析构函数;b.对象成员析构函数;c.基类析构函数。
b.请问静态函数和虚函数的区别?
静态函数在编译的时候就已经确定运行时机,虚函数在运行的时候动态绑定。虚函数因为用了虚函数表机制,调用的时候会增加一次内存开销。
c.请说一哈重载和重写的区别?
重载:两个函数名相同,但是参数列表不同(个数,类型),返回值类型没有要求,在同一作用域中
重写:子类继承了父类,父类中的函数是虚函数,在子类中重新定义了这个虚函数,这种情况是重写
d.c++实现多态实现的机制?
C++的多态实现是基于继承和虚函数,这种实现称为动态多态性。多态性可以简单的概括为“1个接口,多种方法”,在程序运行的过程中才决定调用的机制,简单的说就是可以通过父类指针调用子类的函数,可以让父类指针有多种形态。
e.如果定义了两个函数,一个带const,一个不带,会有问题吗?
不会,这相当于函数的重载。
f,请问虚函数表是怎么实现多态的?
子类若重写父类虚函数,虚函数表中,该函数的地址会被替换,对于存在虚函数的类的对象,在VS中,对象的对象模型的头部存放指向虚函数表的指针,通过该机制实现多态。
g.请问c++中拷贝函数的形参能否进行值传递?
不能。如果是这种情况下,调用拷贝构造函数的时候,首先要将实参传递给形参,这个传递的时候又要调用拷贝构造函数。。如此循环,无法完成拷贝,栈也会满。
h.请说说fork、exec、wait函数?
父进程产生子进程使用fork拷贝出来一个父进程的副本,此时只拷贝了父进程的页表,两个进程都读同一块内存,当有进程写的时候使用写实拷贝机制分配内存,exec函数可以加载一个elf文件去替换父进程,从此父进程和子进程就可以运行不同的程序了。fork从父进程返回子进程的pid,从子进程返回0.调用了wait的父进程将会发生阻塞,直到有子进程状态改变,执行成功返回0,错误返回-1。exec执行成功则子进程从新的程序开始运行,无返回值,执行失败返回-1
i.说一说常见的设计模式?
单例模式:单例模式主要解决一个全局使用的类频繁的创建和销毁的问题。单例模式下可以确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。单例模式有三个要素:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。
工厂模式:工厂模式主要解决接口选择的问题。该模式下定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,使其创建过程延迟到子类进行。
观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。