嵌套类

在函数和其他类定义外定义的类称为全局类,即在全局位置定义的类,反之就是内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class Line{
public:
class Point{
public:
Point(int x,int y)
:_ix(x)
,_iy(y)
{}
friend ostream & operator<<(ostream & os,const Point &rhs);
private:
int _ix;
int _iy;
};
public:
Line(int x,int y,int z,int h)
:_pt1(x,y)
,_pt2(z,h)
{}
friend ostream & operator<<(ostream & os,const Line &rhs);
private:
int _ix;
int _iy;
int _iz;
int _ih;
private:
Point _pt1;
Point _pt2;
};

//为了用输出流运算符输出line对象
//其中访问了Line的私有数据成员(_pt1,_pt2)所有要设为line的友元
ostream & operator<<(ostream & os,const Line &rhs){
os << rhs._pt1<<"--->"<<rhs._pt2 << endl;
return os;
}
//为了用输出流运算符输出Point对象
//其中访问了Point的私有数据成员(_ix,_iy)所以要设为Point的友元,
//而且在形参中不能直接使用Point类型名称,需要加上Line的类名作用域,如果Point是Line的私有成员,那还需要在Line里面声明该函数的友元
ostream & operator<<(ostream & os,const Line::Point &rhs){
os << Point._ix << "," << Point._iy << endl;
}



void test(){
Line::Point pt(1,2);//无法直接创建Point对象,要加作用域,并且如果设为私有也无法创建
cout << ll << endl;
}

嵌套类结构的访问权限

外部类访问内部类

外部类无法直接访问内部的私有成员(包括静态私有成员),需要友元声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Line{
public:
class Point{
public:
Point(int x,int y)
:_ix(x)
,_iy(y)
{}
//friend class Line;
private:
int _ix;
int _iy;
static int _iz;//应该在哪里初始化呢 1.Point之外Line之内 发现不行
//在Line之外才可以
};
public:
Line(int x,int y,int z,int h)
:_pt1(x,y)
,_pt2(z,h)
{}
//在Line的成员函数中访问Point的普通私有成员
void getPoint(){
cout << _pt1._ix <<endl;//error 无法访问Point的私有成员,如果要访问也需要在Point中声明友元
Point::_iz;//外部类访问内部类的私有成员也不能直接访问,要访问需要声明友元
}
private:
int _ix;
int _iy;
int _iz;
int _ih;
private:
Point _pt1;
Point _pt2;
};
int Line::Point::_ix = 0;

内部类访问外部类

可以直接访问外部类的私有成员,甚至可以直接使用Point的数据成员访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Line{
public:
class Point{
public:
Point(int x,int y)
:_ix(x)
,_iy(y)
{}
//friend class Line;
void getLine(const Line &rhs){
rhs._pt1;//可以直接访问外部类的私有的非静态的成员
rhs._line;//可以直接访问外部类的私有的静态成员
//还可以这么写
Line::_line;
//甚至可以
_line;//类似于全局类的成员函数中访问全局变量(个人认为就是把Point看成和Line的成员函数同等级别就可以,是一个特殊的成员函数)
}
private:
int _ix;
int _iy;
static int _iz;
};
public:
Line(int x,int y,int z,int h)
:_pt1(x,y)
,_pt2(z,h)
{}

}
private:
int _ix;
int _iy;
int _iz;
int _ih;
private:
Point _pt1;
Point _pt2;
static int _line;
};
int Line::_line = 0;

内部类天然是外部类的友元类,但是外部类可不是内部类的友元类

pimpl模式

头文件只给出接口

1
2
3
4
5
6
7
8
9
10
//Line.hpp
class Line{
public:
Line(int x1, int y1, int x2, int y2);
~Line();
void printLine() const;//打印Line对象的信息
private:
class LineImpl;//类的前向声明
LineImpl * _pimpl;
};

(2)在实现文件中进行具体实现,使用嵌套类的结构(LineImpl是Line的内部类,Point是LineImpl的内部类),Line类对外公布的接口都是使用LineImpl进行具体实现的

​ 在测试文件中创建Line对象(最外层),使用Line对外提供的接口,但是不知道具体的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//LineImpl.cc
class Line::LineImpl
{
class Point{
public:
Point(int x,int y)
: _ix(x)
, _iy(y)
{}
//...
private:
int _ix;
int _iy;
};
//...
};

//Line.cc
void test0(){
Line line(10,20,30,40);
line.printLine();
}

(3)打包库文件,将库文件和头文件交给第三方

1
2
3
4
5
6
7
sudo apt install build-essential
g++ -c LineImpl.cc
ar rcs libLine.a LineImpl.o

生成libLine.a库文件
编译:g++ Line.cc(测试文件) -L(加上库文件地址) -lLine(就是库文件名中的lib缩写为l,不带后缀)
此时的编译指令为 g++ Line.cc -L. -lLine

单例对象的自动释放

方法一:利用另一个对象的生命周期管理资源

就是利用一个栈对象来托管堆对象,这样就可以在该对象被销毁的时候自动调用析构函数来回收堆对象的空间了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Singleton{
...
friend class AutoRelease;
};

class AutoRelease{
public:
AutoRelease(Singleton *p)
:_p(p)
{}
~AutoRelease(){
if(_p){
delete _p;
_p = nullptr;
}
}
private:
Singleton *_p;
}

void test(){
AutoRelease ar(Singleton::getInstance());
}

如果定义两个 AuroRelease来托管同一个资源,那么就会出现double free的问题

方式二:嵌套类+静态对象

如果可以确保只能定义出来一个AutoRelease那么就可以解决double free的问题了,要使其唯一除了可以使用单例模式,还可以用内部类+静态对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Singleton{
...
public:
class AutoRelease{
public:
AutoRelease()
{}
~AutoRelease(){
if(_pInstance){
delete _pInstance;
_pInstance = nullptr;
}
}
private:
Singleton *_p;
}
...
private:
...
static Singleton *_pInstance;
static AutoRelease _as;//要使用静态的 因为不设置静态的,这个指针就跟单例类的其他数据成员放在一起了,单例对象要销毁的时候就会触发销毁_as 而销毁_as就会导致内部类中的销毁单例对象,一直循环调用了
};
...//初始化静态单例类的_pInstance
Singleton::AutoRelease SingLeton::_ar

方法三:atexit+destory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdlib.h>

void show(){
cout << "whoe()" << endl;
}
void display(){
cout <<"display()" << endl;
}
void test0(){
atexit(show);//被注册的函数会在程序正常结束时被调用

atexit(show);//一个函数被多次注册会被多次调用

atexit(dispalay);//先注册的后调用
}

void test1(){
test0();
dispaly();
}

用在单例对象里

1
2
3
4
5
6
7
8
9
10
class Singleton{
...
static Singleton * getInstance(){
atexit(destory);
if(nullptr == _p){
_p = new Singleton();
}
reutrn _p;
}
};

三种方法都无法保证多线程的安全,因为当多个线程同时进入getInstance的if语句里,会造成单例对象被创建出来多个,然后只有一个地址值会保存在_PInstance上这样只有一份是安全的,其他的都被泄露了

懒汉式:体现的是懒加载,用到单例对象的时候才创建,单线程没问题,但是多线程不安全

饿汉式单例模式

1
2
//static Singleton *_pInstance;的初始化
Singleton * Singleton::_pInstance = Singleton::getInstance();//这样既可以确保这次一定是先被调用的,这样就可以保证后面线程调用getInstance不会进入if里面