C++总结——类

初始化列表

一般是能和赋值初始化互用的,以下情况只能用初始化列表,不能用赋值初始化:

  • 如果对象成员对应的类没有默认构造函数,那对象成员也只能在初始化列表进行初始化。

  • 如果类是继承而来,基类没有默认构造函数的时候,基类的构造函数要在派生类构造函数初始化列表中调用。

  • const成员的初始化只能在构造函数初始化列表中进行

  • 引用成员的初始化也只能在构造函数初始化列表中进行

关键字public private protected

修饰类的成员

  • 在类的内部(定义类的代码内部),无论成员被声明为 publicprotected 还是 private,都是可以互相访问的,没有访问权限的限制。
  • 在类的外部(定义类的代码之外),只能通过对象访问成员,并且通过对象只能访问 public 属性的成员,不能访问 privateprotected 属性的成员。

修饰继承方式

public继承方式
  • 基类中所有 public 成员在派生类中为 public 属性
  • 基类中所有 protected 成员在派生类中为 protected 属性
  • 基类中所有 private 成员在派生类中不能使用
private继承方式
  • 基类中的所有 public 成员在派生类中为 protected 属性
  • 基类中的所有 protected 成员在派生类中为 protected 属性
  • 基类中的所有 private 成员在派生类中不能使用
protected继承方式
  • 基类中的所有 public 成员在派生类中均为 private 属性
  • 基类中的所有 protected 成员在派生类中均为 private 属性
  • 基类中的所有 private 成员在派生类中不能使用

构造函数

C++编译器至少会为我们写的类提供以下几个函数:

  • 一个默认构造函数(无参)
  • 一个拷贝构造函数
  • 一个析构函数
  • 赋值运算符
    如果用户自己定义了有参构造函数 编译器将不再提供无参构造函数 但任会提供拷贝构造函数
    如果用户自己定义了拷贝构造函数,编译器将不再提供默认无参构造函数与默认的拷贝构造函数

拷贝构造函数与移动构造函数

拷贝构造函数

作用:主要是实现类的深拷贝(内存的拷贝),防止浅拷贝出现的内存多次释放的问题。

class A {
public:
	A() {
		num = new int(10);
	}
	~A() {
		delete num;
	}
	int* num;
};

当程序中出现如下语句时,程序会崩溃

A a;
A b(a);

原因:系统提供的拷贝构造函数是浅拷贝,只是简单的将所有的数据进行值复制。因此对象a与b中的int指针的值也一样,即a与b中的int指针指向了同一块区域,于是当析构函数释放申请的内存时,就会出现同一块内存被释放两次的情况,导致程序崩溃
在这里插入图片描述
拷贝构造函数就是为了解决这个问题。我们需要手动重写拷贝构造函数,自己完成值拷贝

A(const A& a) {
	num = new int(*a.num);
}

移动构造函数
作用:减少深拷贝造成的不必要复制,与移动语义和右值引用有关
右值与左值引用

struct与class
  1. 默认的继承访问权。class默认的是private,strcut默认的是public
  2. 默认访问权限:struct作为数据结构的实现体,它默认的数据访问控制是public的,而class作为对象的实现体,它默认的成员变量访问控制是private的
  3. class这个关键字还用于定义模板参数,就像typename。但关建字struct不用于定义模板参数

空类的大小为1

多态

静态多态

包括函数重载函数模板。静态多态是由编译器在编译期间完成的。
函数重载:重载函数的关键是函数参数列表。包括:函数的参数数目和类型,以及参数的排列顺序。
函数的返回值不作为函数重载的依据(只有返回值不同的两个函数不能发生重载)

动态多态

动态多态是程序在运行时动态绑定函数实现多态。
派生类配合虚函数,由父类指针指向子类对象,实现运行时多态。
虚函数与纯虚函数

  • 子类的虚函数可以不被子类重写,但父类的纯虚函数必须被子类重写
  • 含有纯虚函数的类叫抽象类,抽象类不能实例化对象,而含有虚函数不含有纯虚函数的类可以实例化对象

构造函数、析构函数、虚函数

结论:C++中,构造函数不可以是虚函数,而析构函数可以且常常是虚函数。

  • 首先,构造函数没必要时虚函数
    在调用构造函数时还不能确定对象的真实类型(由于子类会调父类的构造函数);并且构造函数的作用是提供初始化,在对象生命期仅仅运行一次,因此,构造函数没必要是虚函数
  • 其次,构造函数也不能是虚函数
    虚函数的调用是通过虚函数表来实现的。而虚函数表是由类的实例化对象的vptr指针去寻找的。也就是说一个类的虚函数表需要类对象去寻找。那么我们就必须先创建并初始化好一个对象实例,而类的构造函数就是用来创建初始化实例对象的,这就产生了矛盾:
    要想实例化对象我们就必须知道虚函数表在哪,这样才能完成初始化,而虚函数表又必须由实例化好的对象的vptr指针才能找到
    这两个变成了一种死锁的状态,因此,构造函数不可以是虚函数
  • 我们使用虚函数往往是用来实现多态的,而多态是由父类指针指向子类对象实现的,那么在析构的时候如果不重写父类的析构函数,那么只会调用父类的析构函数,而父类的析构函数只会释放父类空间,那么子类的一些空间就没办法释放,造成内存泄漏。
class Father {
public:
	Father() {
		cout << "父类构造函数被调用" << endl;
	}
	~Father() {
		cout << "父类析构函数被调用" << endl;
	}
};
class Son : public Father {
public:
	Son() {
		cout << "子类构造函数被调用" << endl;
	}

	~Son() {
		cout << "子类析构函数被调用" << endl;
	}
};
Father* fa = new Son();
delete fa;
输出:
父类构造函数被调用
子类构造函数被调用
父类析构函数被调用

若将父类的析构函数改成虚函数,输出:
父类构造函数被调用
子类构造函数被调用
子类析构函数被调用
父类析构函数被调用
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值