C++面向对象高级编程(二)

本系列为根据侯捷老师系列课程所编写的笔记。

1. 三大函数:拷贝构造,拷贝复制,析构函数

class String
{
public:
	String(const char* cstr = 0);
	String(const String& str);//拷贝构造
	String& operator = (const String& str)
	~String();
	char* get_c_str() const {return m_data;};
private:
	char* m_data;
};

(1)拷贝构造

普通的构造函数:

inline String::String(const char* cstr = 0)
{
	if(cstr)
	{
		m_data = new char[strlen(cstr)+1];
		strcpy(m_data, cstr);
	}else
	{
		m_data = new char[1];
		*m_data = '\0';
	}
}
深拷贝和浅拷贝的区别:

浅拷贝:仅将指针进行了拷贝;
深拷贝:将指针指向的内容进行拷贝。

拷贝构造函数(深拷贝):

inline String::String(const String& str)
{
	m_data = new char[strlen(str.m_data) + 1];
	strcpy(m_data, str.m_data);
}

(2)拷贝赋值

步骤:
1.销毁原先所存储的值;
2.创建一个与赋值变量大小相同的空间;
3.将变量的值拷贝进来;
4.返回指向自身的*this。

inline String& String::operator = (const String& str)
{
	if(this == &str)//自我赋值
	{
		return *this;
	}
	delete[] m_data;
	m_data = new char[strlen(str.m_data) + 1];
	strcpy(m_data, str.m_data);
	return *this;
}

(3)析构函数

析构函数调用时机:
1.变量在离开其作用域时被销毁;
2.当一个对象被销毁时,其成员被销毁;
3.容器被销毁时,其元素被销毁;
4.对于动态分配的对象,当对指向它的指针应用delete运算符时被销毁;
5.对于临时对象,当创建它的完整表达式结束时被销毁。

inline String::~String()
{
	delete[] m_data;
}

2.堆、栈与内存管理

:存在于某作用域的一块内存空间。例如调用函数时,函数本身会形成一个stack用来放置它所接收的参数,以及返回地址。
stack object的生命周期:栈中对象的在作用域结束时就会被销毁(但有种例外,如果对象被声明为static,那么其直到程序结束后才会被销毁)。
:由操作系统提供的一块全局的内存空间,程序可动态分配从中获取若干区块。
heap object的生命周期:其生命周期在指针被delete之际结束。

new

new:先分配内存,再调用构造函数

Complex* pc = new Complex(1, 2);

被转换为

void* mem = operator new(sizeof(Complex));//分配内存,内部调用malloc
pc = static_cast<Complex*>(mem);//把空类型指针转换为目标类型指针
pc->Complex::Complex(1, 2);//构造函数

delete

delete:先调用析构函数,再释放内存

String* ps= new String("Hello");
delete ps;

转换为

String::~String(ps);//析构函数
operator delete(ps);//释放内存,内部调用free(ps)

动态分配的内存块

一个Complex类的内存分配
在每个区块上头放个cookie,记录分配内存块的长度。
array new所分配的内存必须搭配array delete销毁。
(适用于带有指针的类,但不带指针的类用这个方法也是个好习惯)

m_data = new char[10];
delete[] m_data;

若不搭配array delete,则只会调用一次析构函数,即只删除了第一个元素。

String* p = new String[3];
delete[] p;//调用三次析构函数

String* p = new String[3];
delete p;//调用一次析构函数

3.补充知识

静态static

非静态函数通过this指针来判断当前调用该函数的对象是哪个
静态函数和静态数据在程序里只有一份
静态数据:当所有类的实例都用同一个数据时,可使用静态数据
静态函数:没有this指针,只能存取静态数据

class Account
{
public:
	static double m_rate;
	static void set_rate(const double& x){m_rate = x;}
};
double Account::m_rate = 8.0;
int main()
{
	Account::set_rate(5.0);//调用static函数的方法一:通过类名调用

	Account a;
	a.set_rate(7.0)//调用static函数的方法一,通过对象调用
}

把构造函数放在private区

class A
{
public:
	static A& getInstance();
	setup(){...}
private:
	A();
	A(const A& rhs);
	...
};

A& A::getInstance()
{
	static A a;
	return a;
}

template 模版

在编译时替换其中的类型
函数模版:

template <class T>
inline const T& min(const T& a, const T& b)
{
	return b < a ? b : a;
}

编译器会对函数模版进行实参推导,不必明确指出需要的变量类型。

namespace

将所有东西包装进一个namespace中,可防止有重名的现象。

组合与继承

Composition(复合),表示has-a

template <class T>
class queue
{
	...
protected:
	deque<T> c;
}

queue中有一个deque,这就是复合,表示一个中含有另一个。
queue内部使用deque来实现,将deque改造成queue,所以queue就是一个Adapter(适配器),完成一些特定的任务。

复合关系下的构造和析构

在这里插入图片描述
构造由内而外:要先把内部的Component构造完毕后(编译器自动执行),再调用自己的构造函数。
析构由外而内:外部先把析构函数调用后,再调用内部Component的析构函数。
就和穿衣服一样,要先把内部的衣服穿好了才能穿外部的衣服,反之,要先把外部的衣服脱了才能脱内部的衣服。

委托(delegation),使用reference的复合

class String
{
public:
	...
private:
	StringRep* rep;
};

class StringRep
{
	friend class String;
	StringRep(const char* s);
	~StringRep();
	int count;
	char* rep;
};

和复合差不多,但是委托是使用指针将另一个类包含在其中,所以相当于是一个比较“虚”的复合。在这里插入图片描述

继承(Inheritance),表示is-a

class _List_node_base
{
	_List_node_base* _M_next;
	_List_node_base* _M_prev;
};

template <typename _Tp>
class _List_node : public _List_node_base
{
	_Tp _M_data;	
};

在这里插入图片描述
public继承,表示派生类是一种基类,父类数据可以完全继承下来(就好像继承传家宝一样)
继承和复合类似,也是派生类中包含着一个父类,所以其构造和析构和复合一致:
构造由内而外:先调用基类的默认构造函数,再调用自身的构造函数;
析构由外而内:先调用自身的析构函数,在调用基类的构造函数。
基类的析构函数必须是virtual,否则会出现undefined behavior

继承中的虚函数

派生类继承的是父类中函数的调用权
非虚函数:不希望派生类重新定义(override,重写)它;
虚函数:希望派生类重新定义(重写)它,且对它已有默认定义;
纯虚函数:希望派生类重新定义(重写)它,但对它没有默认定义。

class Shape
{
public:
	virtual void draw() const = 0;//纯虚函数
	virtual void error(const std::string& msg);//虚函数
	int objectID() const;//非虚函数
}

在调用函数的时候,是使用this指针进行的,所以派生类的指针能找到相对应的函数。

复合+继承关系下的构造和析构

构造:先构造基类,再构造派生类,所以先调用基类的构造函数,再调用复合类的构造函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值