使用C++的时间也有几年了,但是自己的C++基础还是不过关,为了能够提高自己的编程能力,更好的发掘C++的强大,又重新回到书本寻找那些被自己遗忘的宝藏。本文只是自己学习笔记,编排较乱,主要包含类的继承和传值与传引用的区别。
类的继承:
1.派生类通过继承基类,使得基类成员也成为派生类的成员,能够在派生类中访问基类成员(根据继承方式和访问控制级别所决定),通过这种方式重用基类代码。
2.一个类中成员的可访问性由其访问控制(类自身的public,protected,private控制),而派生类中基类成员的可访问性则还要由继承方式决定。
3.类自身访问控制:
访问控制 | public | protected | private |
类自身 | 可以 | 可以 | 可以 |
继承类 | 可以 | 可以 | 不可以(隐藏) |
类对象 | 可以 | 不可以(隐藏) | 不可以(隐藏) |
继承方式\
自身访问控制
| public | protected | private |
public | 成为派生类公有成员 | 成派生类的保护变量 | 成派生类的不可访问变量(私有财产不容侵犯) |
protected | 派生类的保护变量 | 派生类的保护变量 | 成派生类的不可访问变量(私有财产不容侵犯) |
private | 成为派生类私有变量 | 成为派生类私有变量 | 成派生类的不可访问变量(私有财产不容侵犯) |
5.如果派生类和基类出现重名变量或者方法时,需要通过域名控制符::确定访问对象,默认访问自身的该变量和方法。
6.关于C++中派生类、基类的各种对象的构造函数和初始化的机制,可以总结如下:
(1)对基类成员和子对象成员的初始化必须在成员初始化列表中进行,新增成员的初始化既可以在成员初始化列表中进行也可以在构造函数体中进行。
(2)如果所有基类和类中子对象都存在无参的默认构造函数时,派生类也不需要参数,可以不在派生类构造函数初始化成员列表中显示调用基类和子对象的构造函数,这时将会通过各自的默认构造函数初始化基类和子对象。初始化顺序是:先初始化基类,然后初始化子对象,最后初始化派生类非类对象。
@1 调用基类构造函数;
@2 调用子对象的构造函数;
@3 派生类的构造函数体。
7.当派生类有多个基类时,处于同一层次的各个基类的构造函数调用顺序取决于定义派生类时声明的顺序(自左向右),而与在派生类构造函数的成员初始化列表给出的顺序无关。
8.如果派生类的基类也是一个派生类,则每个派生类只负责其直接基类的构造即可,依次上溯。
9.当派生类有多个子对象时,各个子对象的构造函数的调用顺序取决于这些对象的声明在派生类中出现的顺序(自前至后),而与在派生类构造函数的成员初始化列表中给出的顺序无关。
10.对象析构时,析构函数的调用顺序与构造函数的调用顺序相反(先压栈后出栈)。
虚继承:
当派生类继承多个类,而基类又继承自同一个基类时,为了明确成员变量,这时需要采用虚继承。如B继承自A,C继承自A,而D同时继承了B和C,这时B和C就需要采用虚继承的方式,则A称为虚基类,从而使得在D中只存在一份A的拷贝。继承方式采用加上关键字virtual:
class A
{
char a;
public:
A(char cc=‘A’):a(cc){}
~A(){}
}
class B:virtual public A
{
char b;
public:
B(char cc = 'B'):b(cc){}
~B(){}
}
class C:virtual public A
{
char c;
C(char cc='C'):c(cc){}
~C(){}
}
class D:public B,public C
{
char d;
public:
D(char cc='D'):d(cc){}
~D(){}
}
在虚拟继承下,派生类D中就只存在一份A的拷贝。在虚继承下,构造函数的调用次序跟虚拟继承不同,其执行次序遵循下面几条规则:
(1)虚基类的构造函数最先调用,然后才调用非虚基类的构造函数
(2)如果类的同一继承层次上包含了多个虚基类,则他们的构造函数的调用顺序跟继承的先后次序一致。如果某个虚基类的构造函数在前面被调用了(直接或间接),则不会再次调用;
(3)如果虚基类继承自非虚的类,则先调用虚基类的基类构造函数,在调用虚基类的构造函数
传值与传引用的区别:
1.在函数调用时,经需要进行参数传递和返回,在这不讨论基本的数据类型,对于基本的数据类型,传值和传引用开销差距不大,但是如果参数是类对象时,传值和传引用的差距就可能会很明显。
2.函数形参(传值与传引用):当函数形参采用传值的方式进行时,将会在函数调用时调用实参类的拷贝构造函数临时生成一个匿名对象,当函数结束时将析构。当采用传引用时,函数调用时不会调用类的拷贝构造函数,因为这传引用其实是将实参的地址传递给了函数,因而函数内部可以直接对实参进行操作。换句话说相当于扩大了实参原本的作用域。
3.函数返回值(传值与传引用):
如果函数返回一个对象,系统会在该函数的返回点(主调函数中)调用对象所属类的拷贝构造函数,把返回的对象作为拷贝构造函数的实参,创建一个匿名的临时对象。该匿名对象在使用之后即刻习销毁。
同样,函数可以返回对象指针或者对象的引用,但是注意:函数不能返回非静态对象的指针或引用(简称局部对象)。因为局部对象是局部变量的一种,在函数调用期间存在于栈上,函数返回后,该对象就被销毁了(函数返回后出栈,原空间回收),对应的内存空间被系统释放并在后面需要时分配给其他数据结构对象,所以对应的指针或地址也就无效了。函数能够返回的对象指针(引用)要么是指向函数中动态创建的对象(存在于堆空间上),要么指向全局对象,要么指向函数的指针函数所指向的对象,要么是该类本身(运算符重载时常用)。