C++单例设计模式
一.单例设计模式(重点)
1.运用场景
LCD设备,日志系统,需要频繁使用的设备或者系统,一般都会封装成单实例
2.单例实现
1.先把构造函数设置私有属性
2.实现单例的接口必须是static修饰的类成员函数
3.例子
class LcdDevice
{
public:
~LcdDevice(){cout << "~lcddevice"<<endl;}
// 实现单例接口,实例化对象
static LcdDevice* getInstance();
// 销毁
static void destroy();
void show() {cout << "show"<<endl;}
private:
LcdDevice(){} //构造函数私有
private:
static LcdDevice *instance;
};
// 初始化静态属性(必须外部初始化)
LcdDevice* LcdDevice::instance = NULL;
// 实现单例接口,实例化对象(这里不用写static)
LcdDevice* LcdDevice::getInstance()
{
if(instance == NULL)
instance = new LcdDevice(); //申请空间
return instance;
}
// 销毁设备
void LcdDevice::destroy()
{
if(instance != NULL)
{
delete instance;
instance = NULL;
}
}
int main()
{
// 实例化lcd
LcdDevice* lcd = LcdDevice::getInstance(); //获取空间
lcd->show();
LcdDevice::destroy(); //静态函数直接类名调用
}
//--------------------------
4.单实例总结
只要在类里面将构造函数设为私有属于,那么此类一定是单实例类,就是单例设计模式
//--------------------------
二.友元
1.概念
友元是c++独有特性,设置了友元函数或者友元类后,可以访问对应类的私有或保护成员
2.友元特点
1.优点 : 可以在对应友元后的函数中访问类里面的所有成员属性或成员方法(public,protected,private)
适当的提高了程序的运行效率
2.缺点 : 破坏类的封装性,违背面向对象思想
3.友元函数定义
friend 成员方法
friend void show();
4.友元函数使用
1.声明友元接口,此接口可以放在类里面任意位置声明
class Data
{
public:
Data(int a) : age(a){}
private:
int age;
void print();
// 声明函数为友元
friend void show();
};
2.友元函数类外实现
因为友元函数不属于类成员函数,没有this指针,所以类外实现不需要添加添加从属符(:😃,可以访问类的所有成员
3.友元函数类内实现,实现的时候也是需要创建类对象的
class Data
{
public:
Data(int a) : age(a){}
private:
int age;
void print();
// 声明函数为友元
friend void show()
{
Data d(10);
cout << d.age << endl;
d.print();
}
};
//注意使用的时候需要类外声明
void show();
//-----------------------
int main()
{
// 友元函数
show();
}
练习1:
设计一个点类Point,私有成员 int x,int y
设计一个非成员函数distance(point &a,point &b)
计算两点的直线距离
#include <math.h>
sqrt() ;// 开根号
例子:
class Point
{
public :
Point(int x,int y) : x(x),y(y){}
friend void mc_instance(Point &a,Point &b);
private:
int x;
int y;
};
// 实现友元接口
void mc_instance(Point &a,Point &b)
{
// sqrt 是开根号
double ret = sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
cout << ret << endl;
}
int main()
{
Point a(0,0);
Point b(1,1);
mc_instance(a,b);
}
//----------------------------------
5.类成员访问另外一个类的所有属性,只设计类中的某个函数可以访问另一个类的所有属性
例如 : A::show()要访问B成员,把A::show()设置为B的友元函数
需要在A类的上方添加一个前向声明 class B;
class B;// 前向声明
前向声明 -- 只能用于类的声明,但是不能访问类的成员,如果要访问,必须先
创建B类后再访问
例子:
// 前向声明
// 只是声明有B这个类,但是不能访问类里面的成员,如果要访问类里面的成员
// 需要在类创建后实现
class B;
class A
{
public:
A(){}
public:
void show(B& b);
};
// 错误的,因为还没有执行到B类
// void A::show(B& b)
// {
// b.age = 10;
// cout << b.age <<endl;
// }
class B
{
public:
B(){}
public:
void print(){}
private:
int age;
// 将A类的方法设置为B类的友元
friend void A::show(B& b);
};
// 正确用法,要创建B类后实现
void A::show(B& b)
{
b.age = 10;
cout << b.age <<endl;
}
int main()
{
B b;
A a;
a.show(b);
}
//-------------------------
6.友元类
将一个类设置另一个类的友元,格式:
class A
{
private:
int age;
// 将B设置A的友元
friend class B;
}
class B
{
}
友元类总结:
友元是单向的,比如将A类声明为B类的友元类,那么A类可以访问B类的所有成员
但是,B类不能访问A类的私有或保护成员
设置为友元类后,此类任意方法可以访问另一个类的所有属性
//--------------------
例子:
class A
{
public:
void showA(B& b)
{
b.age = 20;
cout << b.age << endl;
}
private:
int age;
// 将B类设置为友元类,这样B里面的成员就可以访问A里面的私有方法或者私有属性
// 友元类声明,已经声明了B类
friend class B;
};
class B
{
public:
void showB(A &a)
{
a.age = 10;
cout << a.age << endl;
}
int a;
private:
int age;
// 将A类设置为友元类,这样A里面的成员就可以访问A里面的私有方法或者私有属性
friend class A;
};
int main()
{
A a;
B b;
b.showB(a);
}
//----------------------------------
练习2:
设计一个点类Point,私有成员 int x,int y
设计一个计算类Calc,类中有mc_distance(point &a,point &b)
计算两点直接的直线距离
写法一 : 将Calc设置为Point类的友元类
class Point
{
public:
Point(int x,int y):x(x),y(y){}
private:
int x;
int y;
// 将计算类设置为友元类
friend class Calc;
};
class Calc
{
public:
void mc_distance(Point &a,Point &b);
private:
int x;
int y;
};
void Calc::mc_distance(Point &a,Point &b)
{
double ret = sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
cout << ret << endl;
}
写法二 : 将Calc的mc_disstance设置为Point的友元函数
// 前向声明
class Point;
class Calc
{
public:
void mc_distance(Point &a,Point &b);
private:
int x;
int y;
};
class Point
{
public:
Point(int x,int y):x(x),y(y){}
private:
int x;
int y;
// 将计算类mc_distance设置为友元函数
// 注意Calc类存在后才能使用类里面的成员
friend void Calc::mc_distance(Point &a,Point &b);
};
void Calc::mc_distance(Point &a,Point &b)
{
double ret = sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
cout << ret << endl;
}
int main()
{
Point a(0,0);
Point b(1,1);
Calc ca;
ca.mc_distance(a,b);
}
二.友元类不能继承
基类的友元函数不能被派生类继承。但是当派生类是通过公有继承方式派生时,默认地通过强制类型转换,可以将派生类转换为基类,从而调用友元函数。证明了派生类与基类是 is -a 的关系。但是,如果派生类是通过私有继承时,则不能够调用基类的友元函数。
友元类不能访问子类
//C++三态:封装 继承 多态
class A
{
private:
int Adata;
friend class B;
};
class B
{
public:
void show(A &a1)
{
a1.Adata = 10;
cout << a1.Adata << endl;
}
};
class C : public B
{
public:
void Cshow(A &a1)
{
// 友元类属性不能被子类继承,子类不能访问父类对应友元类的私有属性
a1.Adata = 100; // 错误
}
};
友元关系不能继承。
1.基类的友元对派生类的成员没有特殊访问权限。
2.如果基类被授予友元,则只有基类具有特殊访问权限,该基类的派生类无特殊访问权限;
3.但该基类没有被派生类重写且有特殊访问的成员函数仍能被派生类对象直接调用。
//----------------------------#
三.运算符重载
1.场景引入
int a = 1,b = 2;
int c = a + b;
class A{}; class B{}; A a; B b; a+b
2.通过关键字 operator 运算符重载符,通过oeprator既可以实现运算符重载
3.能实现运算符重载的符号
双目运算符 (+ - * / %)
关系运算符 (== != < > <= >=)
逻辑运算符 (|| && !)
位运算符 (| & ~ ^ << >>)
单目运算符 (++ -- * &)
赋值运算符 (= += -= )
空间申请运算符 (new delete)
其它运算符 (() -> [])
4.不能实现运算符重载
.(成员访问运算符)
::(从属运算符)
sizeof
?: (条件运算符,三目运算符)
5.运算符重载格式
返回值 operator重载符号 (参数)
6.双目(+)重载
1.重载方式----成员重载
A a3 = a1+a2;--> 成员重载 类似于 A a3 = operator(A a1) + (A a2)
规定;加号的左值是对应的 A operator + (A a2) 函数的对象
加号右值为A operator + (A a2)的函数参数 所以函数A operator + (A a2)的
this指针是 加号左值的this指针
2.例子
class A
{
public:
A(int a) : a(a){}
// 成员重载 类似于 A a3 = operator(A a1) + (A a2)
// 如果在类里面实现重载,那么加号(+)左值直接用operator替代,加号(+)右值用(参数传递)
A operator + (A a2)// 等价于 A a3 = a1 + a2
{
cout << "operator+" <<endl;
A a3(0);
// this->a == a1.a + a2.a
a3.a = this->a + a2.a;
return a3; zuo
}
public:
int a;
};
int main()
{
int a,b;int c = a + b;
A a1(5);
A a2(10);
// 运算重载 operator.A + a2--->operator.A+(a2)--->operator+(a2)
A a3 = a1 + a2; // 运算重载
cout << a3.a << endl;
}
练习1 : 创建Point类,实现实部和虚部两个数值相减号
Point p1(10,10); Point p2(20,20);
Point p = p2 - p1;
cout << p.x << p.y << endl;
例子:
class Point
{
public:
Point(int x,int y) : x(x),y(y){};
// 函数重载
Point operator - (Point& p2);
public:
int x;
int y;
};
// 函数重载
Point Point::operator - (Point& p2)
{
Point p3(0,0);
p3.x = this->x - p2.x;
p3.y = this->y - p2.y;
return p3;
}
练习2. 实现 A+1
#include <iostream>
using namespace std;
class A
{
public:
A(int data) : data(data){}
// 成员重载加法
friend int operator+ (int b)
{
return this->data + b;
}
public:
int data;
};
int main()
{
A a(10);
int b = a + 1;
cout << b << endl;
}
//---------------------------------------------------
7.友元重载
friend operator 重载符(参数1,参数2)
规定 : 加号左值为第一个参数(参数1)加号右值为第二个参数(参数2)
例子:
class A
{
public:
A(int d):data(d){}
friend int operator+ (int b,A& a);
private:
int data;
};
// 友元重载,相当于普通函数调用
int operator+ (int b,A& a)
{
cout << "friend operator"<<endl;
return b + a.data;
}
int main()
{
A a(10);
int b = 2+a;
cout << b << endl;
}
练习3:
将Point实现友元重载
Point p1;
Point p2;
Point p3 = p1 + p2
例子:
class Point
{
public:
Point(int x,int y) : x(x),y(y){}
// 普通的成员函数不能用this指针
friend Point operator+(Point& p1,Point& p2)
{
Point p3(0,0);
p3.x = p1.x + p2.x;
p3.y = p1.y + p2.y;
return p3;
}
public:
int x;
int y;
};
// 总结:
成员重载 --- 加号左值一定是类的对象
class A
{
int operator+(int b)
{
this->data;
}
int data;
};
A a;a+1;
友元重载 --- 加号的左值不一定是类的对象
友元重载后的函数不属于类成员函数
class A
{
friend int operator+(A &a,int b)
{
a.data;
}
int data;
};
A a;a+1;
//--------------------
输入输出重载
输出重载方式
class D{};
D d;
实现 cout<<d << endl; 流程如下
1.将cout<<d进行<<运算符重载
friend ostream& operator<< (ostream& out,D d)
{
out << d.data;
reutrn out;
}
为什么要return ostream & 因为 如果我们 cout << d << endl;是需要重载后的返回值
此返回值为ostream才能匹配到 cout<<endl;// 系统重载方式
如果没有返回值 就变成了 void << endl;// 系没有实现这个功能所以报错
例子:
class Data
{
public:
Data(int d) : data(d){}
friend ostream& operator<< (ostream& out,Data& d)
public:
int data;
};
// 重载cout
ostream& operator<< (ostream& out,Data& d)
{
out << d.data;
return out;
}
int main()
{
Data d(10);
Data d1(20);
//func() = 123;
// 1.将cout进行重载
cout << d;
// 2.因为系统默认重载
cout << endl;
// 3.所以将out进行返回
cout << d << << d1 << endl;
}
//----------------------------
输入重载
例子:
class Data
{
public:
Data(int d) : data(d){}
// 输出重载
friend ostream& operator<< (ostream& out,Data& d);
// 输入重载
friend istream& operator>> (istream &in,Data& d);
public:
int data;
};
// 输入重载
istream& operator>> (istream &in,Data& d)
{
in >> d.data;
}
// 输出重载cout
ostream& operator<< (ostream& out,Data& d)
{
out << d.data;
return out;
}
int main()
{
Data d(10);
Data b(20);
// 输入重载
cin >> d >> b;
//输出
cout << d <<" "<< b <<endl;
}
//-----------------------------
单目运算符 ++ – --> A++
单目运算符用成员重载
注意++A 的重载函数不需要添加参数
Data operator++ ()
1.A++
例子:
// 重载Data++
Data operator++ (int)
{
// 创建一个临时对象保存原来的数值
Data old(number);
this->number++;
return old;
}
//-------------------------
2.++A
例子:
// 重载++Data
Data operator++ ()
{
Data da(0);
da.number = ++this->number;
return da;
}
//-----------------------------
重载[]
char buf[10] = “hello”
buf[0] = ‘h’;
例子:
#include <iostream>
using namespace std;
class Array
{
public:
Array(int n) : length(n)
{
this->ptr = new int[this->length];
}
~Array()
{
delete []ptr;
ptr = NULL;
}
// 重载[]
int& operator[] (int i)
{
this->count = i;
return this->ptr[count];
}
friend ostream& operator<< (ostream& out,Array& d)
{
out << d.ptr[d.count];
return out;
}
private:
int length;
int *ptr;
int count;
};
int main()
{
Array arr(10);
arr[0] = 200;
cout << arr[0] << endl;
}
//-----------------------
用运算符重载实现数据类型转换
Data da(10);
int age = da;
注意:
数据类型转换实现重载,重载函数没有返回值没有参数,重载方式如下:
operator int()
例子:
class Data
{
public:
Data(int age) : age(age){}
// 数据类型转换重载
operator int ()
{
return age;
}
private:
int age;
};
int main()
{
Data da(100);
int age = da;
cout << age << endl;
}
//---------------end--------------------