C++面向对象高级编程 part2
@(boolan C++)[C++]
2017-10-22 14:17:56 / helingchao
概述
本章节内容主要讲述如何设计class with pointer,以string为例。
#ifndef __MYSTRING__
#define __MYSTRING__
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;
};
#include <cstring>
inline
String::String(const char* cstr)
{
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()
{
delete[] m_data;
}
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;
}
inline
String::String(const String& str)
{
m_data = new char[ strlen(str.m_data) + 1 ];
strcpy(m_data, str.m_data);
}
#include <iostream>
using namespace std;
ostream& operator<<(ostream& os, const String& str)
{
os << str.get_c_str();
return os;
}
#endif
big three
所谓big three即:
- 拷贝构造
- 拷贝赋值
- 析构
1. 编译器提供的默认函数
声明一个空类
声明一个空类,如下:
class Empty {
}
C++会为上面定义的Empty类,提供默认的成员。效果等同于如下定义:
class Empty {
public:
Empty() {...};
~Empty(){...};
Empty(const Empty& rhs){...};
Empty& operatpr = (const Empty& rhs) {...};
}
C++提供的默认函数
- default 构造函数
- copy构造函数
- 析构函数
- copy assignment函数
原则 01: C++默认提供的函数仅在被调用时,编译器才会创建它们。
注意:这四个函数都是public和inline的。
默认函数的行为都是什么
default构造和析构函数
default 构造函数和析构函数主要是给编译器一个地方放置“藏身幕后”的代码。
- default构造函数 :调用base classes和non-static 成员变量的构造函数。
- 析构函数 :调用base classes和non-static 成员变量的析构函数。
注意: non-static成员的说法,static 类成员不在default构造函数的初始化范围内。
copy构造函数和copy assignment
单纯的将对象的每一个non-static成员拷贝到目标对象。
用户需要重新定义copy构造函数和copy assignment的场景
类中包含以下类型成员
- 引用类型
- 指针类型
- const类型
2. 字符串设计
1509260994792.png- 用指针保存存储数据的地址,不用数组。好处在于灵活的存储,无需考虑设计多大的数组。
实际是一种动态存储思想与静态存储思想之间的决策。
3. ctor & dtor
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] = '\0';
}
}
inline string::~string() {
delete[] m_data;
}
字符串的两种形式:
- 串+‘\0’
- 长度字段+串
4. copy ctor & copy op =
1509261365517.png浅拷贝 vs. 深拷贝
在copy对象时仅copy指针成员属于浅copy, 创建新的内存并将指针的内容copy到新内存是深copy。
在class with pointer 默认的copy ctor & copy op = 使用的是浅拷贝。
浅拷贝造成的问题:
浅拷贝会造成 alias(别名) 和 memory leak
alias 与 野指针/悬空指针
单一个内存单元被多个对象所只向时,可以看作是该内存单元存在了别名。
// p_a 与p_b 构成了alias
int *p_a = new int();
int *p_b = p_a;
alias引入的问题:
内存单元被释放时,其他地点的别名可能不知道该内存以被释放,继而继续使用该内存,产生不确定的行为(undefined behavior)。
什么是悬空指针(dangling pointer)?
If a pointer still references the original memory after it has been freed, it is called a dangling pointer.
悬空指针是指针最初指向的内存已经被释放了的一种指针。
什么是野指针(wild pointer)?
A pointer in c which has not been initialized is known as wild pointer.
野指针(wild pointer)就是没有被初始化过的指针.
5. 拷贝赋值/copy assignment operator
string& string::operator = (const string& str_r) {
if (this == &str_r) { // 自我检测
return *this;
}
delete[] m_data;
m_data = new char[strlen(str_r.m_data) + 1];
strcpy(m_data, str_r.m_data);
return *this;
}
注意⚠️:
拷贝赋值中的自我检测
copy op=的动作:
- 清空已有的数据
- 创建新内存
- 拷贝
拷贝构造与拷贝赋值的差别
- 拷贝构造是创建新对象,拷贝已有对象到新对象
- 拷贝赋值时,两个对象都已经被创建。
基于上述两点拷贝构造无需检测自我赋值,但拷贝赋值需要。
内存管理
1. stack vs. heap
1509263493997.pngstack, 是存在于某个作用域的一块内存空间。 例如函数被调用时,函数本身就会生成一个stack。
heap,是指由操作系统提供的一块gloabl内存空间,可由程序员动态创建。
2. static object & global object
两者在生命周期都是在main结束之后,程序结束之后才结束。
3. new: 先创建内存,后调用ctor
1509262367694.pngnew的三个步骤
// 原始语句
complex* pc = new complex(1,2);
// 编译器内部转换后的遇见
void* mem = opreator new(sizeof(complex)); // 1. 分配内存
pc = static_cast<complex*>(mem); // 2. 类型转化
pc->complex::complex(1,2); // 3. 调用构造函数
成员函数的调用过程
哪个对象调用类成员函数,(编译器)默认将该对象的指针传给被调用成员函数this。
pc->complex::complex(1,2);
complex::complex(pc,1,2);
3. delete:先调用dtor, 后释放内存
1509262410676.png4. array new 一定要搭配 array delete
1509262539128.png扩展补充:类模版..
1. C++对象内存模型
- non-static 数据成员
- static 数据成员
- non-static 成员函数
- static 成员函数
- static 数据成员/static成员函数/none -static成员函数 全局只有一份。
- none-static数据成员每个对象各有一份
this指针
哪个对象调用成员函数,哪个对象的指针就会作为参数传递给成员函数的this指针
complex c1;
c1.real();
complex::real(&c1);
- none-static 成员函数通过this指针将处理不同对象的non-static数据。
- this pointer如何传递:non-static 成员函数是全局唯一的,哪个对象调用ns成员函数就将哪个对象的地址作为this指针传递给ns成员函数
static 成员
static成员脱离于对象。成员函数也是脱离于对象。
- static 成员函数没有this pointer
- st 成员函数只能处理 st数据
- st成员函数没有this指针所以不能处理非st数据
静态数据要在classbody外定义,定义时不要添加static声明。
class acount{
static int a;
}
int acount::a; // 类st成员定义
- st 成员数据要在class body 外“定义”。这里是定义,因为要分配内存。
- static 成员函数的两种调用方式:object调用,classname 调用。
设计static成员的原则
如果数据是在对象间通用的,则将该数据设计为类的static成员。
static 版本的singleton
class singleton {
public:
static singleton& get_instance() {
static singleton a;
return a;
}
private:
singleton() {};
}
cout
1509263864546.pngclass template
1509263880945.pngfunction template
1509263907947.pngargument deduction (实参推导):
- argument deduction (实参推导):不同于class template , function template 在使用时不用指定具体类型
网友评论