拷贝构造与赋值操作
拷贝构造是一种特殊的构造函数,格式为:
类名(const 类名& that) // 可以但不限制const
{
}
什么时候调用拷贝构造:
当使用旧对象给新对象初始化时,会自动调用拷贝构造
Test t;
Test t1 = t; // 会自动调用拷贝构造
拷贝构造的任务:
顾名思义拷贝构造负责把旧对象中的成员变量拷贝给新对象,且编译器默认就会生成具有该功能的函数
什么时候应该显式实现拷贝构造:
普通情况下编译器生成的拷贝构造完全够用,当类中成员有指针且为指针分配了堆内存,默认拷贝构造只 会把指针的值进行拷贝,这样就会使两个对象的指针指向同一块内存,这样在析构时就会造成重复释放堆 内存导致内存奔溃,所以这种情况应该显式地实现拷贝构造。
深拷贝与浅拷贝
当类中的成员有指针且为指针分配了堆内存
浅拷贝(默认的拷贝构造)只拷贝指针变量的值
深拷贝(显式实现的靠别)不拷贝指针的值,而是拷贝指针指向的内容
赋值操作:
所谓的赋值操作,就是一个对象给另一个对象赋值时(两对象都已经创建完毕),在C++中会把运算符当作函数,使用运算符时会调用运算符函数
类名& operator = (const 类名& that)
{
}
Test t1,t2;
t1 = t2;
赋值运算符函数的任务:
它与拷贝构造的任务基本一致,默认情况编译器也会自动生成一个具有浅拷贝功能的赋值运算符函数
当需要深拷贝时不仅需要编写显式赋值函数,还要编写显式拷贝构造函数
实现赋值运算符需要注意的问题:
赋值运算符函数与拷贝构造函数的任务相同,但环境不同
问题1:两个对象的指针变量,指针成员都已经分配堆内存
1、先释放被赋值者的指针变量所指向的堆内存
2、再给被赋值者的指针变量重新分配内存
3、把赋值者的指针变量所指向的内容拷贝到被赋值者的指针变量所指向的内存
问题2:可能出现对象自己给自己赋值
判断this指针和赋值者的地址是否相同,如果相同则立即结束
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
class Student
{
char* name;
char sex;
int id;
public:
Student(const char* name,char sex,int id)
{
int len = strlen(name);
this->name = new char[len+1];
strcpy(this->name,name);
this->sex = sex;
this->id = id;
cout << "我是Student的有参构造" << endl;
}
Student(const Student& that)
{
int len = strlen(that.name);
name = new char[len+1];
strcpy(name,that.name);
sex = that.sex;
id = that.id;
cout << "拷贝构造" << endl;
}
~Student(void)
{
printf("---%p---\n",name);
delete[] name;
}
void show(void)
{
cout << name << " " << sex << " " << id << endl;
}
Student& operator= (const Student& that)
{
if(this != &that)
{
delete[] name;
int len = strlen(that.name);
name = new char[len+1];
strcpy(name,that.name);
sex = that.sex;
id = that.id;
}
return *this;
}
/*
Student& operator=(const Student& that)
{
if(this != &that)
{
Student temp(that); //利用拷贝构造 结束自动释放
swap(temp.name,name);
sex = that.sex;
id = that.id;
}
return *this;
}*/
};
void func(Student stu) //函数可以调用拷贝构造
{
}
int main(int argc,const char* argv[])
{
Student stu("hehe1",'w',18);
//func(stu);
//Student stu1 = stu; // 初始化,调用拷贝构造
Student stu1("xixi1",'m',23);
stu1 = stu; // 调用赋值运算符函数
stu.show();
stu1.show();
}
实现一个String类
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
class String
{
char* str;
public:
String(const char* str="")
{
this->str = new char[strlen(str)+1];
strcpy(this->str,str);
}
String(const String& that)
{
str = new char[strlen(that.str)+1];
strcpy(str,that.str);
}
~String(void)
{
printf("str=%p\n",str);
delete[] str;
}
String& operator=(const String& that)
{
if(this != &that)
{
delete[] str;
str = new char[strlen(that.str)+1];
strcpy(str,that.str);
}
return *this;
}
void show(void)
{
cout << str << endl;
}
};
int main(int argc,const char* argv[])
{
String str("xixihehe");
str.show();
String str1 = str;
str1.show();
String str2;
str2 = str;
str2.show();
}
静态成员
什么是静态成员
被static修饰的成员函数或成员变量叫静态成员
普通成员的特点:
成员变量,每个类对象都有一份成员变量,相互之间没有关联
成员函数,隐藏着一个this指针参数
静态成员的特点:
静态成员变量
1、存储在bss或data内存段中,一个类的静态成员变量只有一份,所有类对象共享
2、必须在类内声明,类外定义、初始化,在定义和初始化时不需要再加static
静态成员函数
1、没有隐藏的this指针,所有在静态成员函数中无法访问普通成员变量和成员函数,但可以直接访问静 态成员变量和静态成员函数
2、虽然不能直接访问成员变量和成员函数,但是静态函数的作用域依然算作类内,因此只要静态成员函 数获得了类对象的指针或引用,依然有权限访问private protected的成员变量、成员函数
3、调用方法:
对象 . 函数名();
类名 : : 函数名()
静态成员的作用:
1、静态成员相当于多了一层类作用域的全局变量、全局函数
2、静态全局变量适合存储所有类对象公共属性,这样可以节约内存资源
3、静态成员函数可以当作静态成员变量的访问接口,另一方面它可以在不破坏封装性的前提下,让一个类 有能力管理自己的对象
C语言中的static和C++中的static的区别
单例模式
只能实例化出一个类对象
适用什么场景:
日志管理器/进程管理器
网站访问计数器
线程池、内存池
连接管理器
实现单例模式的原理:
1、禁止在类的外部创建对象,把构造函数私有化
2、确保类对象只有一份,在类中定义一个静态的指针或类对象
3、提供一个获取类对象的接口,设计静态成员函数用于获取类对象
饿汉模式的单例:
程序运行时就实例化出类对象,不管后期是否用到都已经创建完成
优点:不可能被多线程同时运行时创建多份
缺点:如果后期用不到,就浪费了资源
#include <iostream>
#include <pthread.h>
using namespace std;
class Single
{
static Single obj;
Single(void)
{
cout << "我是Single的无参构造" << endl;
}
Single(Single& that) {}
public:
static Single& get_single(void)
{
return obj;
}
void show(void)
{
cout << &obj << endl;
}
};
Single Single::obj;
int main(int argc,const char* argv[])
{
Single &s = Single::get_single();
}
懒汉模式的单例:
直到真正使用时才创建对象
优点:什么时候用什么时候建,如果用不到则不会创建,节约资源
缺点:多个线程同时创建可能会创建出多份
#include <iostream>
#include <pthread.h>
using namespace std;
class Single
{
static Single* obj;
Single(void)
{
cout << "我是Single的无参构造" << endl;
}
Single(Single& that) {}
public:
static Single& get_single(void)
{
if(NULL == obj)
{
obj = new Single;
}
return *obj;
}
void show(void)
{
cout << &obj << endl;
}
};
Single* Single::obj;
void* run(void* arg)
{
Single& s = Single::get_single();
}
int main(int argc,const char* argv[])
{
for(int i=0; i<100; i++)
{
pthread_t tid;
pthread_create(&tid,NULL,run,NULL);
}
}