C++08(类和对象)

类和对象

0.C/C++的区别

C语言:面向过程的语言
C语言:计算机语言
C语言的缺陷就是把属性和行为分离设计

  1. 计算机语言设计的目的和意义是什么:
    通过计算机来解决现实中的问题(模拟现实)
  2. 模拟现实中的什么?
    结构体–>实体属性,函数—>行为
  3. 现实中的东西叫实体—》用属性和行为来描述
    C++:面向对象的语言

0.1.C语言结构体

C语言结构体

typedef struct Data
{
	int ma;
	int mb;
}DATA;
void SetValue(DATA* pdt,int a,int b)  //赋值
{
	pdt->ma=a;
	pdt->mb=b;
}
int main()
{
	DATA val;
	setValue(&val,10,20);
	return 0;
}

0.2把变量和函数联系在一起

把变量和函数联系在一起---->函数指针

typedef struct Data
{
	int ma;
	int mb;
	void(*func)(struct Data*, int, int);
}DATA;
//void (*) (struct Data*,int,int);
void SetValue(DATA* pdt, int a, int b)
{
	pdt->ma = a;
	pdt->mb = b;
}
int main()
{
	DATA val;
	val.func = &SetValue;
	val.func(&val, 10, 20);  //可以理解为实体在执行行为
	//val.func(10,20);
	return 0;
}

在这里插入图片描述

1.类和对象

在这里插入图片描述

1.1 oop思想(面向对象语言的思想)

在这里插入图片描述

1.2写一个人类

//class 类标识  
class People
{
public:
	void eat(char* what)
	{
		std::cout << mname << " is eatting " << what << std::endl;
	}
	void sleep(char* _where)
	{
		std::cout << mname << " is sleepping  at" << _where << std::endl;
	}
	void play()
	{
		std::cout << mname << " is playing  doudou!" << std::endl;
	}
private:
	char mname[10];//姓名
	bool msex;//性别
	int mage;//年龄
};
/*
	设置点起  到类结束或者下一个访问限定符为止  
*/
/*
	类默认的访问限定符 
		private:
*/
int main()
{
	//int a;//类型  生成  变量 
	People p1;//People  类型   类类型   类型  实例化  对象
	//访问限定符,导致访问失败
	//strcpy_s(p1.mname, strlen("xiaoming"), "xiaoming");
	p1.eat("肉");
	return 0;
}

2.C++三大特征

C++三大特征 :
封装 继承 多态

访问限定符
从设置点起,到类结束或者下一个访问限定符为止,默认为private
1.public: 任意访问
2.protected: 子类和本类类中
3.private: 本类类中

2.1封装(该让你看的能看见,不该让你看的看不见)

封装就是: 属性和行为实现保护起来

流程应该是:要钱,好,然后小明从口袋拿钱递给我
属性是私有的,是需要保护的
行为是公有的,public
在这里插入图片描述
设计一个学生类

#include<iostream>
class Student
{
private:
	char name[10];
	bool sex;
	int age;
public:
	void learn(const char* what)//
	{
		std::cout << name << "learning" << what << std::endl;
	}
	void eat(const char* when, const char* what)
	{
		std::cout << name << "eating" << what << "on" << when << std::endl;
	}
	void sleep(const char* when, const char* what)
	{
		std::cout << name << "sleeping at" << what << "on" << when << std::endl;
	}
};

2.2.成员变量和成员方法-----和对象的关系?

在这里插入图片描述

3.this

在这里插入图片描述
this指针指向对象的说法太片面,this指针指向 对象所占的空间(没有资源)
在这里插入图片描述
this指针不允许修改,指向不能发生变化(自身存储的地址不能变)
在这里插入图片描述

3.1thiscall调用约定

成员方法调用依赖对象调用
在这里插入图片描述

4.类中默认的函数

如果设计者没有提供,那么系统会提供六个默认函数
1.构造函数
2.析构函数
3. 拷贝构造函数
4. 赋值运算符的重载函数
5. 取地址操作符的重载函数
6. const修饰的取地址操作符的重载函数

4.1.C语言中的类的初始化和销毁

类的初始化函数:
在这里插入图片描述
类的销毁函数:
在这里插入图片描述
主函数调用
在这里插入图片描述

4.2C++中的构造函数和析构函数

4.2.1构造函数

构造函数的作用:
初始化对象所占的内存空间(赋予对象所占的内存空间资源)
构造函数(可以重载,“生而不同”)
有this指针(如果没有的话,没法确定给哪一个成员变量做初始化)
不可以人为调用,只能系统调用(构造函数是thiscall调用,不依赖对象调用,构造函数没有调用完成,对象是不会生成的)
构造函数没有调用完成:对象不完整----->半成品对象
在这里插入图片描述
在这里插入图片描述
生成对象的步骤

  1. 开辟对象所占的内存空间<-----系统提供的
  2. 调用构造函数(初始化对象所占的内存空间)<-----系统提供的
    对象是什么?
    空间+资源
    变量是什么?
    空间
    定义和实例化的区别?
    定义----->空间(开辟)
    实例化---->空间+资源(开辟+赋予资源)
    系统提供的构造函数应该长什么样子?
    90行出现歧义,91行才是默认构造函数的调用
    在这里插入图片描述

4.2.2析构函数

析构函数(不能重载,“死了都一样”)
析构函数的作用:
释放对象所占的其他资源
析构函数(不能重载,“死了都一样”)
有this指针
可以人为调用

实现:
delete[] mname------>销毁堆上开辟的内容
在这里插入图片描述
调用时间:
对象的销毁:
1.调用析构函数(释放对象所占的其他资源)
2.系统释放对象所占的空间----->栈上的内容

4.2.2构造函数和析构函数区别

1.构造函数的作用:
初始化对象所占的内存空间(赋予对象所占的内存空间资源)
构造函数(可以重载,“生而不同”)
有this指针(如果没有的话,没法确定给哪一个成员变量做初始化)
不可以人为调用,只能系统调用(构造函数是thiscall调用,依赖对象调用,构造函数没有调用完成,对象是不会生成的)
2.析构函数的作用:
释放对象所占的其他资源
析构函数(不能重载,“死了都一样”)
有this指针
可以人为调用,但是会退化成普通函数调用,程序结束后会再调用一次。
3.区别:
生来赋予的属性是不可以修改的,不能人为调用。
而消亡的过程可以人为操控。
4.构造函数和析构函数的顺序:
先构造后析构
开辟空间是在栈里,1先入栈然后23入栈,栈是先入后出,所以销毁时候,3先出栈。
在这里插入图片描述

4.2.3练习:C++封装链表(友元关系)

友元关系:
1.单向性

#include<iostream>

class CLink;  //前置声明
class Node //结点类
{
public:
	Node(int data = 0);//给一个默认值
private: //如果把私有改成公有,数据的保护无法完成
	int mdata;
	Node* next;
	friend class CLink;
};
Node::Node(int data)//函数名前加类的作用域--》表示函数是类的一个成员方法
{
	mdata = data;
	next = NULL;
}

class CLink    //链表类--》结点的集合
{
public:
	CLink();
	~CLink();
	//头插 尾插 头删 尾删 打印
	void InsertOfHead(int val);
	void InsertOfTail(int val);

	bool Empty();
	bool DeleteOfHead();//要判断是否为空
	bool DeleteOfTail();

	void Print();

private:
	Node* phead;
};
CLink::CLink()
{
	phead = new Node();  //头指针指向头结点
}
CLink::~CLink()
{
	//释放头结点和数据节点
	Node* p = phead;
	Node* tmp;
	while (p != NULL)
	{
		tmp = p->next;
		delete p;
		p = tmp;
	}
	phead = NULL;
}
void CLink::InsertOfHead(int val)
{
	Node* s = new Node(val);
	s->next = phead->next;
	phead->next = s;
}
void CLink::InsertOfTail(int val)
{
	Node* s = new Node(val);
	Node* tail = phead;
	while (tail->next != NULL)
	{
		tail = tail->next;
	}
	tail->next = s;
}

bool CLink::Empty()
{
	//头结点的指针域是否为空
	return phead->next == NULL;
}
bool CLink::DeleteOfHead()
{
	if (Empty())
	{
		return false;
	}
	Node* q = phead->next;
	phead->next = q->next;
	delete q;
}
bool CLink::DeleteOfTail()
{
	if (Empty())
	{
		return false;
	}
	//删除最后一个节点,最后一个的前一个的指针应该指向为空
	Node* tail0 = phead;//倒数第二个节点
	Node* tail = tail0->next;//最后一个节点
	while (tail->next != NULL)
	{
		tail0 = tail0->next;
		tail = tail0->next;
	}
	tail0->next = NULL;
	delete tail;
	return true;

}

void CLink::Print()
{
	Node* p = phead->next;
	while (p!= NULL)
	{
		std::cout << p->mdata << " ";
		p = p->next;
	}
	std::cout << std::endl;
}
int main()
{
	CLink cl;
	for (int i = 0; i < 5; ++i)
	{
		cl.InsertOfHead(i + 1);
	}
	cl.Print();
	for (int i = 0; i < 5; ++i)
	{
		cl.InsertOfTail(i + 1);
	}
	cl.Print();
	cl.DeleteOfHead();
	cl.Print();
	cl.DeleteOfTail();
	cl.Print();

	return 0;
}

4.3拷贝构造函数

用一个已经存在的对象来生成相同类型的新对象
必须用引用当形参,防止递归调用拷贝构造函数

class CGoods
{
public:
	CGoods()
	{
	}
	CGoods(int amount)
	{
		mamount = amount;
	}
	CGoods(char* name, float price, int amount)
	{
		mname = new char[strlen(name) + 1]();
		strcpy_s(mname, strlen(name) + 1, name);
		mprice = price;
		mamount = amount;
	}
	//深拷贝
	CGoods(const CGoods& rhs)
	{
		mname = new char[strlen(rhs.mname) + 1]();
		strcpy_s(mname, strlen(rhs.mname) + 1, rhs.mname);
		mprice = rhs.mprice;
		mamount = rhs.mamount;
	}

	/*
	1. 防止形参修改实参的值
	2. 接收隐式生成的临时量----》必须用常引用来接收临时量(常量)
	*/
	//void  CGoods&  
	void operator=(const CGoods& rhs)//this:左操作数  形参:右操作数
	{
		if (this == &rhs)//自赋值
		{
			return;
		}
		delete[] mname;
		mname = new char[strlen(rhs.mname) + 1]();
		strcpy_s(mname, strlen(rhs.mname) + 1, rhs.mname);
		mprice = rhs.mprice;
		mamount = rhs.mamount;
	}

	~CGoods()
	{
		delete[] mname;
		mname = NULL;
	}
private:
	char* mname;
	float mprice;
	int mamount;
};

int main()
{
	CGoods good1("面包", 4.5, 100);

	CGoods good2 = good1;----->拷贝构造函数

	CGoods good2("牛奶", 2.5, 1000);

	good2 = good1;//编译器通过-》赋值运算符的重载函数,进行赋值----->operator,详细解释见4.4


	return 0;
}

4.3.1拷贝构造函数

拷贝构造函数
在这里插入图片描述
在这里插入图片描述

4.3.2浅拷贝构造函数

默认拷贝构造函数:浅拷贝(如果成员变量有指针,考虑是否实现深拷贝)
指向了同一个内存区域,good2销毁时,释放了mname内存,当good1销毁时候,释放内存就会出现重复释放错误。
在这里插入图片描述

4.3.3为什么拷贝构造函数的形参一定是引用&

拷贝构造函数如果形参不加引用,递归调用生成形参对象,导致栈溢出,程序崩溃
在这里插入图片描述

4.4. 赋值运算符的重载函数(浅拷贝,要实现自己写的深拷贝版本)

用一个已存在的对象赋给相同类型的已存在对象
构造的步骤:
1.判断是否是自赋值
2.释放旧资源
3.开辟新资源
4.赋值
在这里插入图片描述

4.4.1面试常问的几个问题

4.4.1.1.class和struct的区别

默认访问限定符不同:

   class:
        private
   struct:
	     public
4.4.1.2.空结构体的大小多大,为什么? 空类呢?

C:(用模子来刻出内存块,空结构体相当于不存在的模子,是无法刻画内存块的)
不能定义空结构体

C++:
1 个字节
结构体是当类类型来处理的

空类:
1

类模拟的是抽象概念,是从实体中抽象出来的,实体是现实中存在的,因此,C++设计者将空类的大小默认设置为1,有空间的对象就能模拟出实体

struct Data
{
public:
	void Show()
	{
		std::cout << "hello world" << std::endl;
	}
};

//只有成员方法没有成员变量,也被认为是空类
class Test
{
public:
	void Show()//_thiscall 
	{
		std::cout << "hello world!" << std::endl;
	}
};

int main()
{
	Test test;//如果是0  则test不存在的对象
	test.Show();
	//Data val;
	//val.Show();
	//std::cout << sizeof(struct Data) << std::endl;
	return 0;
}

4.4.1.3.赋值运算符的返回值类型
class CGoods
{
public:
	CGoods()
	{
		mname = new char[1]();
	}
	CGoods(char* name, float price, int amount)
		:mname(new char[strlen(name) + 1]()),
		mprice(price),
		mamount(amount)
	{
		strcpy_s(mname, strlen(name) + 1, name);
	}
	CGoods(const CGoods& rhs)
	{
		mname = new char[strlen(rhs.mname) + 1]();
		strcpy_s(mname, strlen(rhs.mname) + 1, rhs.mname);

		mprice = rhs.mprice;
		mamount = rhs.mamount;
	}
	CGoods& operator=(const CGoods& rhs)
	{
		if (this != &rhs)
		{
			delete[] mname;
			mname = new char[strlen(rhs.mname) + 1]();
			strcpy_s(mname, strlen(rhs.mname) + 1, rhs.mname);

			mprice = rhs.mprice;
			mamount = rhs.mamount;
		}
		return *this;
	}
	~CGoods()
	{
		delete[] mname;
		mname = NULL;
	}
private:
	char* mname;
	float mprice;
	int mamount;
};
int main()
{
	int a = 10;
	int b = 20;
	int c;
	c = a = b;//连续赋值,自右向结合性

	CGoods good1("面包", 4.5, 100);
	CGoods good2("牛奶小面包", 4.5, 100);

	CGoods good3;
	good3 = good2 = good1;
	//good2 = good1;  //返回值应该是good2本身 ,才能正确的赋给good3
	return 0;
}

在这里插入图片描述

4.5. 取地址操作符的重载函数


4.6. const修饰的取地址操作符的重载函数


5.临时对象

临时对象:
生存周期:表达式结束,遇到分号,生存周期就结束了

5.1临时对象的优化

临时对象的优化:
在这里插入图片描述
在这里插入图片描述

5.2临时对象的分类

  1. 显式生成临时对象:
    明确指出生成怎么样的临时对象
  2. 隐式生成临时对象:
    编译器推演出应该生成怎样的临时对象
    80行是隐式生成,82行是显式生成(只有类型,没有对象名称,就是显式生成)
    在这里插入图片描述

5.3临时量的属性

  1. 内置类型-----》常量
  2. 自定义类型----》变量
  3. 隐式生成的临时对象----》常量

5.4引用能提升临时对象的生存周期

引用会把 临时对象的生存周期 提升的和 引用变量的生存周期 相同
95行的代码:把临时对象的地址赋给指针的指向,当语句结束后临时对象销毁,指针指向的就是无效对象
在这里插入图片描述

6.对象的生存周期

CGoods ggood1("g1", 4, 10);//.data 程序加载到程序结束后销毁
static CGoods ggood2("g2", 2, 10);//.data---》整个程序结束后销毁 程序加载到程序结束
  
int main()
{
	CGoods lgood1;//stack  
	CGoods lgood2("l2", 10, 20);//stack  
	static CGoods lgood3("l3", 30, 10);//.data---》整个程序结束后销毁

	CGoods lgood4 = CGoods("l4", 40, 10);//stack  
	CGoods lgood5 = 20;//stack  (有优化产生,只生成了lgood5对象)
	
	lgood1 = CGoods("tmp", 20, 30);----》不是生成新对象,所有没有优化,表达式完成临时量销毁,有构造,赋值,析构调用
	lgood2 = 30;
	lgood3 = (CGoods)(10,20,30,40);//强转    逗号表达式  {}

	/*
		逗号表达式  
			数据最终只选择最后一个
	*/

	CGoods* pc1 = new CGoods("pc1", 10, 20);//堆  
	CGoods* pc2 = new CGoods[2];//堆 有两个对象生成

	CGoods* pc3 = &CGoods("pc3", 10, 20);


	CGoods& rc3 = CGoods("rc3", 10, 20);

	delete pc1;
	delete[] pc2;
	return 0;
}
CGoods ggood3("g3", 3, 10);//.data

7.类类型返回值:都是通过临时对象带出来

非类类型返回值方式:
1.<=4 eax
2. >4 && <=8 eax edx
3.>8 临时量

问有几个对象生成?
答:五个,test1,test2,arg,tmp,return时候的返回值对象。

class Test
{
public:
	Test(int a = 0)
	{
		ma = a;
	}
	Test(const Test& rhs)
	{
		ma = rhs.ma;
	}
	~Test()
	{
		std::cout << "Test::~Test()" << std::endl;
	}
private:
	int ma;
};

Test getObject(Test arg)
{
	Test tmp = arg;
	//...
	return tmp; //临时量
}
int main()
{
	Test test1(20);
	Test test2;

	test2 = getObject(test1);
	return 0;
}

在这里插入图片描述
在这里插入图片描述

class Test
{
public:
	Test(int a = 0)
	{
		std::cout << "Test::Test(int)" << std::endl;
		ma = a;
	}
	Test(const Test& rhs)
	{
		std::cout << "Test::Test(const Test&)" << std::endl;
		ma = rhs.ma;
	}
	void operator=(const Test& rhs)
	{
		std::cout << "Test::operator=(const Test&)" << std::endl;
		if (this == &rhs)
		{
			return;
		}
		ma = rhs.ma;
	}
	~Test()
	{
		std::cout << "Test::~Test()" << std::endl;
	}
private:
	int ma;
};
/*
	自定义类型做形参  
		一般都会设为引用(引用不生成对象,少了一个构造和析构) 
		效率提高
*/
Test getObject(Test& rhs)--->引用不生成
{
	Test tmp = rhs;//2
	//...
	return tmp;//返回了一个对象  
}
int main()
{
	Test test1(10);//1
	Test rt = getObject(test1);//3---》返回值传过来,产生优化,只生成rt
	return 0;
}

7.1构造函数的初始化列表

在这里插入图片描述
在这里插入图片描述

8.const和static修饰类成员的用法

8.1 const修饰

  1. 修饰成员变量:一定要初始化
  2. 修饰成员方法:常方法

常对象不能调用普通方法,常对象只能调用常方法(普通方法有修改常变量值的风险)
普通对象能调用常方法

常方法中不能调用普通成员方法
普通成员方法中能调用常方法

class Test
{
public:
	Test(int a) : ma(a)
	{}
	void Show()const  //const Test* const this;
	{
		std::cout << "ma:" << ma << std::endl;
	}
	void Print()
	{
		Show();
		std::cout << "hello world!" << std::endl;
	}
private:
	int ma;
};
int main()
{
	Test test(10);
	test.Show();
	return 0;
}

在这里插入图片描述

8.2 面试问题:简述一下const

1.变量:C/C++
2.不允许修改请求处理
3.const修饰成员变量

8.3 static修饰成员

1.修饰成员变量:
初始化方式:成员变量不属于对象 ,属于类 (属于所有对象共享),一定要在类外初始化
访问:不依赖对象访问
2.修饰成员方法:
_cdecl 没有this指针 不能访问普通的成员变量
只能访问:全局变量 静态的成员变量
访问:不依赖对象访问

静态的成员方法不能调用普通的成员方法
普通的成员方法能调用静态的成员方法
构造函数初始化对象所占的内存空间
静态成员变量不属于对象,因此构造函数不能初始化静态成员变量

class Test
{
public:
	Test(int b)
		: mb(b)
	{}
	static void Show()//cdecl 没有this指针  
	{
		//Print();
		std::cout << "ma:" << ma << std::endl;
		//std::cout << "mb:" << mb << std::endl;
	}
	void Print()//thiscall
	{
		std::cout << "mb:" << mb << std::endl;
		Show();
	}

	static int ma;
private:
	int mb;
};
int Test::ma = 10;
int main()
{
	Test test1(10);
	Test test2(20);

	std::cout << test1.ma << std::endl;
	std::cout << Test::ma << std::endl;//
	test1.Show();
	Test::Show();
	test1.Print();
	return 0;
}

在这里插入图片描述

8.4函数指针在调用点调用函数方法

//int (*) (int,int) 
int Sum(int a, int b)
{
	std::cout << "::Sum(int,int)" << std::endl;
	return a + b;
}

class Test
{
public:
	Test(int a)
		:ma(a)
	{}
	void Show()
	{
		std::cout << "ma:" << ma << std::endl;
	}
private:
	int ma;
};

/*
	void (*) (); //Test::
*/
/*
	.*  ->*
	函数指针指向类成员方法后,调用时
*/
int main()
{
	int(*Func) (int, int);
	Func = &Sum;
	(*Func)(10, 20);
	//Sum(10, 20);

	Test test(10); ------》依赖对象调用
	void(Test::* cppfunc)() = &Test::Show;
	(test.*cppfunc)();
	Test* ptest = new Test(20);
	(ptest->*cppfunc)();
	return 0;
}

8.5 练习

class String
{
public:
	//NULL
	String(char* ptr)
	{
		assert(ptr!=NULL);
		if(ptr==NULL)
		{
			return;
		}
		mptr = new char[strlen(ptr) + 1]();
		strcpy_s(mptr, strlen(ptr) + 1, ptr);
	}
	String(const String& rhs)
	{
		mptr = new char[strlen(rhs.mptr) + 1]();
		strcpy_s(mptr, strlen(rhs.mptr) + 1, rhs.mptr);
	}
	String& operator=(const String& rhs)
	{
		if (this == &rhs)
		{
			return *this;   //---->左操作数
		}
		delete[] mptr;
		mptr = new char[strlen(rhs.mptr) + 1]();
		strcpy_s(mptr, strlen(rhs.mptr) + 1, rhs.mptr);
		return *this;
	}
	~String()
	{
		delete[] mptr;
		mptr = NULL;
	}
private:
	char* mptr;//
};

int main()
{
	String str("hello");
	String str2 = str;
	str2 = str;
	return 0;
}

在这里插入图片描述

9.单例模式

设计模式 
	单例模式
	类只能生成一个对象
1.屏蔽生成对象的接口
	构造函数放在private
2.提供接口来生成唯一对象
	1.不能类类型的方式返回
	2.static 

在这里插入图片描述

9.1 设计一个校长类(单例模式:懒汉模式)

/*
	一个接口目的是为了生成对象 
		一般都会写出static
*/
class Rector
{
public:
	static Rector* getInstance(char* name, int age, bool sex)//----》获取实例
	{
		if (pre == NULL)
		{
			pre = new Rector(name, age, sex);
		}
		return pre;
	}
private:
	Rector(char* name, int age, bool sex)
	{
		mname = new char[strlen(name) + 1]();
		strcpy_s(mname, strlen(name) + 1, name);
		mage = age;
		msex = sex;
	}

	char* mname;
	int mage;
	bool msex;

	static Rector* pre;//标识唯一对象  不属于对象 属于整个类的作用域
};
Rector* Rector::pre = NULL;

int main()
{
	Rector * pr1 = Rector::getInstance("zhangsan", 45, true);
	Rector * pr2 = Rector::getInstance("zhangsan", 45, true);
	Rector * pr3 = Rector::getInstance("zhangsan", 45, true);



	//Rector re1("zhangsan", 45, true);
	//Rector re2("lisi", 45, true);
	return 0;
}

9.1.1单例模式生成类的 函数值返回类是什么

&、*都可以,因为不生成临时对象(生成临时对象就会产生两个对象,单例模式是唯一的)
构造函数和拷贝构造函数都可以生成对象,因此不仅要考虑构造,拷贝构造也需要考虑,因此9.1的代码不够完善
在这里插入图片描述

9.2把校长类抽象成模型(线程安全)

9.2.1 未加锁

1.不加锁的情况:会产生不安全因素
在这里插入图片描述

9.2.2加锁

2.加锁后,第一次 安全有了保障,但是第二次第三次仍然要加锁解锁,产生了资源消耗
在这里插入图片描述

9.2.3 双重锁

3.面对资源消耗:双重锁单例机制
在这里插入图片描述

9.2.4饿汉模式:贪婪加载

线程是进程的一条执行路径

线程中处理 才生成唯一对象 ----->不安全
线程开启前 生成唯一对象-----》安全

class SingleTon
{
public:
	static SingleTon* getInstance()  //生成一个接口
	{
		return psing;
	}
private:
	SingleTon()
	{}
	SingleTon(const SingleTon&);
	static SingleTon* psing;//.data
};
SingleTon* SingleTon::psing = new SingleTon();//main执行之前

int main()
{
	SingleTon* psingle1 = SingleTon::getInstance();
	SingleTon* psingle2 = SingleTon::getInstance();
	SingleTon* psingle3 = SingleTon::getInstance();
	return 0;
}

9.2.5懒汉模式:延迟加载

class SingleTon
{
public:
	static SingleTon* getInstance()
	{
		if (psing == NULL)//第二次及以后 线程安全  
		{
			lock();
			if (psing == NULL)
			{
				psing = new SingleTon();
			}
			unlock();
		}
		return psing;
	}
private:
	SingleTon()
	{
	}
	SingleTon(const SingleTon&);
	static SingleTon* psing;
};
SingleTon* SingleTon::psing = NULL;

int main()
{
	SingleTon* psingle1 = SingleTon::getInstance();
	SingleTon* psingle2 = SingleTon::getInstance();
	SingleTon* psingle3 = SingleTon::getInstance();
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值