一、Object Based(基于对象) 不带指针(一般用不到析构函数)
头文件中的防卫式声名:
#ifndef __COMPLEX__ //如果没有定义过COMPLEX,则定义
#define __COMPLEX__
...
...
#endif
一、类模板
如类中private定义变量可以先不指定类型
template<typename T>
class complex{
public:
//函数若在类内定义,则成为inline函数
complex(double r=0,double i=0) : re(r),im(i)
{}
complex& operator += {const complex&);
double real() const{
return re;
}
double img() const{
return im;
}
private:
T re,im; //定义的变量设为私有
friend complex& __doapl(complex*, const complex&);
};
complex<double> c1(6,1); //使用模板
二、构造函数:
1、函数名与类名相同
2、创建对象时使用,无返回值;
3、可以有默认实参;
//高级写法
complex(double r=0,double i=0)
: re(r),im(i) //构造函数初始化
{} //括号里进行赋值
三、函数重载
多数发生在构造函数上
complex(double r=0,double i=0)
: re(r),im(i)
{}
complex() : re(0),im(0); //这种重载的构造函数不可以
complex c1;
complex c2(); //创建对象时不知道调用哪个构造函数
四、常量成员函数
double real() const{ //加const不会改变数据
return re;
}
double img() const{
return im;
}
//如果没有定义const,创建const对象时不能调用函数
五、参数传递、返回值传递
引用传递(to const): (const complex&)传引用不希望被改值
传递者无需知道接收者是否以引用形式&接受
inline complex&
__doapl(complex* ths, const complex& r)
{
...
return *ths;
}
六、友元函数
private:
T re,im; //定义的变量设为私有
friend complex& __doapl(complex*, const complex&);
__doapl(complex* ths, const complex& r)
{
ths->re += r.re;
ths->im += r.im; //直接取数据,不需要通过函数
return *ths;
}
相同class中的各个对象互为友元
七、操作符重载
①成员函数
complex::operator += (这里隐藏着this但不能写出来, const complex& r)
{
return __doapl(this,r); //this不可以在参数列写出来,但可以在使用时写出来
②非成员函数
inline complex(complex这个地方为返回类型)
operator + (const complex& x, const complex& y)
{
return complex(real(x)+real(y), img(x)+img(y));
}
不能以引用返回,因为两个复数相加,会创建一个地方来存储值,函数结束后它会消失,以引用传递出去,接收者查看时本体已没有
conj (const complex& x)
{
return complex(real(x), - img(x));
}
#include <iostream.h>
ostream& //返回类型 即cout的类型
operator << (ostream& os, const complex& x)
{
return os << '(' << real(x) << ',' << img(x) << ')';
}
cout<<conj(c1);
cout<<c1<<cong(c1);
例:复数实现
//头文件
#ifndef __COMPLEX
#define __COMPLEX
class complex
{
public:
complex(double r=0, double i=0)
: re(r),im(i)
{ }
complex& operator += (const complex&);
double real() const
{
return re;
}
double img() const
{
return im;
}
private:
double 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)
{
return __doapl(this,r);
}
inline complex
operator + (const complex& x, const complex& y)
{
return complex(real(x)+real(y), img(x)+img(y));
//创建临时对象的时候赋值,返回
}
inline complex
operator + (const complex& x, double y)
{
return complex(real(x)+y, img(x));
}
inline complex
operator + (double x, const complex& y)
{
return complex(real(y)+x, img(y));
}
}
#endif
测试程序
#include <iostream.h>
ostream&
operator << (ostream& os, const complex& x)
{
return os << '(' << real(x) << ',' << img(x) << ')';
二、Object Oriented(面向对象)带指针
字符串
测试程序:
#include "string.h"
#include <iostream>
using namespace std;
int main()
{
String s1("hello");
String s2("world");
String s3(s2); //拷贝构造
cout << s3 << endl;
s3 = s1;//拷贝赋值
cout << s3 << endl;
cout << s2 << endl;
cout << s1 << endl;
}
类定义
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; //指向字符串的指针
};
一、构造函数、析构函数
构造函数
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()
{
delete[] m_data; //动态分配后需要释放内存,不然会造成内存泄漏
}
二、浅拷贝:
String a(“Hello”);
String b(“World”);
b=a;
a里存放指向"Hello"的指针,b里存放指向"World"的指针,执行b=a后,会让b指针指向Hello,那么b原先指向的内容会没有指针指向,造成内存泄露,同时a,b指向同一内容,后续a修改内容也会对b造成影响;
三、深拷贝
拷贝构造函数:
inline
String::String(const String& str)
{
m_data = new char[ strlen(str.m_data) + 1 ];
strcpy(m_data, str.m_data);
}
拷贝赋值函数:
先将左边的清空 ,再创建和右边一样的内存空间,然后将右边的数据拷贝到左边空间
自我赋值,当两个指针指向同一个内容,没有检测自我赋值,执行delete操作后,下一步操作时,str.m_data没有内容
inline
String& String::operator=(const String& str)
{
if (this == &str) //检测自我赋值 如s2 = s1,那么this为s2,str为s1
return *this;
delete[] m_data; //先清空自己
m_data = new char[ strlen(str.m_data) + 1 ];
strcpy(m_data, str.m_data);
return *this;
}
例:字符串实现
头文件
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; //4个字节
};
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()
{
delete[] m_data;
}
inline
String::String(const String& str)
{
m_data = new char[strlen(str.data)+1];
strcpy(m_data,str.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;
}
四、stack(栈)和heap(堆)
1、栈:存在于作用域的一块内存空间,当调用函数时,函数本身会形成一个stack来放置它接收的参数
2、堆:操作系统提供一块global内存空间,动态取得new,可以在程序任何地方动态获得,需要delete释放
static Complex c2(1,2);
其生命在作用域结束时仍然存在,直到程序结束
Complex* p = new Complex;
delete p;
3、new:先分配内存,在调用构造函数
Complex* pc = new Complex(1,2);
过程为:
Complex* pc;
① void* men = operator new(sizeof(Complex)); //分配内存
② pc = static_cast<Complex*>(men); //转型
③ pc->Complex::Complex(1,2); //构造函数
4、delete: 先调用析构函数,再释放内存
① String::~String(ps);
operator delete(ps); //相当于free()
array new要搭配array delete
不然只调用一次析构函数会造成内存泄漏
三、static
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);
Account a;
a.set_rate(7.0);
}
补充:
using std::cout;
cout<< …;
四、复合 Composition 表示has-a
Container---->component
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnL4cTO5ATO0YTM5EDNwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
构造函数:Container的构造函数首先调用Component的默认构造函数,然后再执行自己
析构函数:Container的析构函数首先执行自己,再调用Component的析构函数
五、委托Delegation 用指针指向
Composition by reference
六、Inheritance(继承) 表示is-a
子类(派生类Derived)、父类(基类Base)
构造函数:子类的构造函数首先调用父类的默认构造函数,然后再执行自己
析构函数:子类的析构函数首先执行自己,再调用父类的析构函数
父类的析构函数要加virtual
① 虚函数:成员函数前加virtual //可能有默认定义
函数调用继承的是调用权
纯虚函数:virtual void draw() const = 0; //没有定义
继承和复合关系下的构造和析构
Ⅰ、
构造函数:由内到外
析构函数:由外到内