1. 防卫式声明
为了防止在别的文件里引用该头文件,不讲究include顺序。
第一次引用的时候,会先判断是否被定义过,如果之前没有被定义过,就定义该头文件。避免重复引用Include。
访问权限 access level
在定义一个class的时候,数据使用private权限
构造函数
空构造函数与有默认值的构造函数不能同时出现,因为会迷惑编译器,让编译器不知道选择哪一个构造函数构造。
参数传递
尽量全部传引用,因为快,安全。速度上相当于传指针。
如果不想被修改,就加const
返回值传递
尽量返回引用;
当返回的值对象的空间原本就已经被创建,存在了。就可以返回其引用。但是如果返回的值的空间是不存在的,那么就不能传递引用。因为引用必须是引用某个对象,对象不存在,那么引用就也不存在。
操作符重载
总结
- 数据都放在private下定义,用函数返回值提取数据;
- 参数传递和返回值传递尽量使用引用;
- 能使用const就要使用;
- 构造函数的Initial_list要尽量使用;
大致框架
#ifndef __COMPLEX__
#define __COMPLEX__
#include <cmath>
#include<iostream>
class ostream;
class complex;
complex& __doapl(complex*,const complex&);
complex::function{...};
#endif
complex.h
#ifndef __COMPLEX__
#define __COMPLEX__
#include <cmath>
#include<iostream>
template<typename T>
class complex{
public:
complex (T r = 0,T i = 0): re(r),im(i){
}
complex operator += (const complex& );
double real() const { return re; }
double imag() const { return im; }
private:
T re,im;
friend complex& __doapl(complex*,const complex&);
};
inline complex&
__doapl(complex* ths,const complex& r){
ths->re += r.re;
ths->im += r.im;
return *ths;
}
inline complex&
complex::operator += (const complex& r){ //右边加到左边,右边是不变的,加const
return __doapl(this, r);
}
//全局函数
inline complex
operator + (const complex& x, const complex& y){
return complex( real(x) + real(y),
imag(x) + imag(y) ); //临时对象
}
inline complex
operator + (const complex& x, double y){
return complex( real(x) + y, imag(x) ); //临时对象
}
inline complex
operator + (const double x, complex& y){
return complex( x + real(y), imag(y) ); //临时对象
}
ostream&
operator << (ostream& os,const complex& x){
return os << "(" << real(x) << "," << imag(x) << ")";
}inline double image(const complex& x){
return x.imag(); //返回复数的虚部
}
inline complex&
__doapl(complex& ths,const complex& r){
ths->re += r.re;
ths->im += r.im;
return *th
}
inline complex&
complex::operator += (const complex& r){ //右边加到左边,右边是不变的,加const
return __doapl(this, r);
}
#endif
complex.cpp
#include "complex.h"
#include "iostream.h"
ostream&
operator << (ostream& os,const complex& x){
return os << "(" << x.real() << "," << x.imag() << ")";
}
main.cpp
#include "complex.h"
int main(){
complex<double> c1(2,1);
complex<double> c2;
...
}
2. big three 三大函数:拷贝构造、拷贝赋值、析构
例如:
String s1();
String s2("hello");
String s3(s2);
浅拷贝,赋值拷贝,a,b指针指向同一个内存hello,但是当a或者b发生改变的时候,另一个都会改变;拷贝指针是浅拷贝,分配空间是深拷贝。
拷贝赋值函数
先检查是否是自我赋值: a = a;
if(this == &str){
return *this; //自我赋值,一方面可以提高效率,同时在在自我赋值下可以保证正确性。
}
先确定是否有自我赋值的情况,要不然直接delete就会导致内存泄漏。
总结
string.h
#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;
};
#endif
string_test.cpp
#include "String.h"
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);
}
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;
}
String::~String(){
delete[] m_data;
}
3. 生命周期
在每次开辟新空间的时候,结束时候要释放空间回收,否则就会出现空间还在,但是指向该空间的指针已经被回收,出现野指针。内存泄漏
指向那块内存的指针已经死掉了,但是内存还在,还没释放掉。就是内存泄漏。
4. new
先通过operator new()分配内存,(内部调用malloc),再调用构造函数。
回收内存的时候,是先调用析构函数,再释放内存(内部调用free)。
delete先杀掉字符串本身,也就是指针m_data,第二步回收动态分配的内存;free
5. 类与类之间的关系
复合
委托
定义一个另外的类,某一个功能给另外的类来完成
继承
父类的析构函数必须是virtual,方便子类空间回收,否则会出现内存泄漏。
委托+继承