十七、拷贝构造和拷贝赋值
1.浅拷贝和深拷贝
1)如果类中包含指针形式的成员变量,缺省的拷贝构造函数只是复制了指针本身,而没有复制所指向的内容,这种拷贝方式称为浅拷贝
2)浅拷贝会导致不同的对象之间的数据共享,如果数据在堆区,析构函数还会引发“double free”的错误,导致进程终止,因此必须自己定义一个支持复制指针所指向内容的拷贝构造函数,即深拷贝
参考代码:
#include <iostream>
#include <cstring>
using namespace std;
class String{
public:
String(const char* str){
m_str = new char[strlen(str)+1];
strcpy(m_str,str);
}
//析构函数
~String(void){
delete[] m_str;
}
//拷贝构造(深拷贝)
String(const String& that){
m_str =
new char[strlen(that.m_str)+1];
strcpy(m_str,that.m_str);
}
public:
const char* c_str(void) const{
return m_str;
}
private:
char* m_str;
};
int main(void)
{
String s("hello C++!");
cout << s.c_str() << endl;
String s2 = s;
cout << s2.c_str() << endl;
return 0;
}
2.拷贝赋值
1)当两个对象进行赋值操作时,比如“i3=i2”,编译器会将其处理为“i3.operator=(i2)”成员函数调用形式,该函数被称为拷贝赋值操作符函数,其返回结果是表达式结果
2)如果类中没有定义拷贝赋值函数,那么编译器会提供一个缺省的拷贝赋值函数,但是缺省的拷贝赋值函数和缺省的拷贝构造函数一样,也是浅拷贝,有内存泄露和double free的问题,为了能得到深拷贝的效果,避免问题,必须自己定义深拷贝函数。
类名& operator=(const 类名& that){
if(&that != this){//防止自赋值
//释放旧内存
//分配新内存
//拷贝新数据
}
return *this;//返回自引用
}
注:that对应右操作数,this指向左操作数
参考代码:
#include <iostream>
#include <cstring>
using namespace std;
class String{
public:
//构造函数
String(const char* str){
m_str = new char[strlen(str)+1];
strcpy(m_str,str);
}
//析构函数
~String(void){
delete[] m_str;
}
//拷贝构造(深拷贝)
String(const String& that){
m_str =
new char[strlen(that.m_str)+1];
strcpy(m_str,that.m_str);
}
String& operator=(const String& that){
if(&that != this){
delete[] m_str;
m_str = new char[
strlen(that.m_str)+1];
strcpy(m_str,that.m_str);
/*char* str = new char[
strlen(that.m_str)+1];
delete[] m_str;
m_str = strcpy(str,that.m_str);
*/
/*String tmp(that);
swap(m_str,tmp.m_str);*/
}
return *this;
}
public:
const char* c_str(void) const{
return m_str;
}
private:
char* m_str;
};
int main(void)
{
String s("hello C++!");
cout << s.c_str() << endl;
String s2 = s;
cout << s2.c_str() << endl;
String s3("happy new year!");
s2 = s3;//拷贝赋值
cout << s2.c_str() << endl;
return 0;
}
十八、静态成员
1.静态成员变量
1)语法
class 类名{
static 数据类型 变量名; //声明
};
数据类型 类名::变量名 = 初值; //定义和初始化
2)普通成员变量属于对象,而静态成员变量不属于对象(计算大小时不算他们)
3)普通成员变量在构造时定义和初始化,而静态成员变量需要在类的外部单独的定义和初始化
4)静态成员变量和全局变量类似,在全局区(数据段),可以把静态成员变量理解成被限制在类中的全局变量
5)使用方法:
类名::静态成员变量名; //推荐
对象.静态成员变量名;//和上面等价
参考代码:
#include <iostream>
using namespace std;
class A{
public:
//普通成员变量在构造时定义和初始化
A(int data):m_data(data){}
int m_data;
static int s_data;
};
//静态成员变量需要在类的外部单独定义和初始化
int A::s_data = 20;
int main(void)
{
A a1(10);
cout << "sizeof(a1):" << sizeof(a1)
<< endl;//4
cout << a1.m_data << endl;//10
cout << A::s_data << endl;//20
cout << a1.s_data << endl;//20
A a2(10);
a2.m_data = 11;
a2.s_data = 22;
cout << a1.m_data << endl;//10
cout << a1.s_data << endl;//22
return 0;
}
2.静态成员函数
1)语法
class 类名{
static 返回类型 函数名(形参表){......}
};
2)静态成员函数没有this指针,也没有const属性
3)使用方法(静态成员函数的调用可以不依赖对象)
类名::静态成员函数(实参表); //推荐
对象.静态成员函数(实参表); //和上面等价
注:在静态成员函数中只能访问静态成员,在非静态成员函数中既可以访问静态成员,也可以访问非静态成员
参考代码:
#include <iostream>
using namespace std;
class A{
//普通成员函数在构造时定义和初始化
A(int data):m_data(data){}
static void func1(void)
{
cout << "静态成员变量" << endl;
//cout << m_data << endl;
cout << s_data << endl;
}
void func2(void){
cout << "非静态成员函数" << endl;
cout << m_data << endl;
cout << s_data << endl;
}
int m_data;
static int s_data;
};
//静态成员变量需要在类的外部单独定义和初始化
int A::s_data = 20;
int main()
{
A::func1();
A a1(10);
a1.func2();
}
3.单例模式
1)概念:一个类只允许存在唯一的对象,并提供它访问的方法
2)实现单例模式的方法
①禁止在类的外部创建对象:私有化构造函数(包括拷贝构造)
②类的内部维护唯一的对象:静态成员变量(如果不是静态的就会出现递归创建,而无法确定类的大小)
③提供单例对象的访问方法:静态成员函数(由于非静态成员函数只有在类存在的时候才能使用,但是单例模式下,无法创建对象,所以不可以使用非静态成员函数)
class A{
public:
A& get()
{
return a;
}
private:
A(void);
A(const A& );
int m_data;
static A a;
};
3)创建方式
①饿汉式:单例对象无论用或不用,程序启动即创建
②懒汉式:单例对象用时再创建
参考代码:
饿汉式
//单例模式:饿汉式
#include <iostream>
using namespace std;
class Singleton{
public:
//3)使用静态成员函数获取单例对象
static Singleton& getInstance(void){
return s_instance;
}
void print(void)const{
cout << m_data << endl;
}
private:
//1)私有化构造函数(包括拷贝构造)
Singleton(int data):m_data(data){
cout << "单例对象被创建了" << endl;
}
Singleton(const Singleton&);
private:
int m_data;
//2)使用静态成员变量维护单例对象
static Singleton s_instance;
};
Singleton Singleton::s_instance(1234);
int main(void)
{
cout << "main函数开始执行.." << endl;
Singleton& s1=Singleton::getInstance();
Singleton& s2=Singleton::getInstance();
Singleton& s3=Singleton::getInstance();
cout << "&s1=" << &s1 << endl;
cout << "&s2=" << &s2 << endl;
cout << "&s3=" << &s3 << endl;
s1.print();
s2.print();
s3.print();
//Singleton s4(4321);//应该error
//Singleton s4 = s1;//应该error
return 0;
}
懒汉式:
//单例模式:懒汉式
#include <iostream>
using namespace std;
class Singleton{
public:
//3)使用静态成员函数获取单例对象
static Singleton& getInstance(void){
if(s_instance == NULL){
s_instance=new Singleton(1234);
}
++s_count;
return *s_instance;
}
void print(void)const{
cout << m_data << endl;
}
//单例对象不用时即销毁,什么时候不用?
//最后一个使用者不用时再销毁。
void release(void){
if(--s_count == 0){
delete s_instance;
s_instance = NULL;
}
}
private:
//1)私有化构造函数(包括拷贝构造)
Singleton(int data):m_data(data){
cout << "单例对象被创建了" << endl;
}
~Singleton(void){
cout << "单例对象被销毁了" << endl;
}
Singleton(const Singleton&);
private:
int m_data;
//2)使用静态成员变量维护单例对象
static Singleton* s_instance;
//计数:记录单例对象使用者的个数
static int s_count;
};
Singleton* Singleton::s_instance = NULL;
int Singleton::s_count = 0;
int main(void)
{
cout << "main函数开始执行.." << endl;
//++s_count ==> 1
Singleton& s1=Singleton::getInstance();
//++s_count ==> 2
Singleton& s2=Singleton::getInstance();
//++s_count ==> 3
Singleton& s3=Singleton::getInstance();
cout << "&s1=" << &s1 << endl;
cout << "&s2=" << &s2 << endl;
cout << "&s3=" << &s3 << endl;
s1.print();
s1.release();//--s_count ==> 2
s2.print();
s3.print();
s2.release();//--s_count ==> 1
s3.release();//--s_count ==> 0,delete
return 0;
}
十九、成员指针
1.成员变量指针
1)定义
类型 类名::*成员指针变量名 = &类名::成员变量
2)使用
对象.*成员指针变量名;
注:“.*”被称为直接成员指针解引用操作符
对象指针->*成员指针变量名;
注:“->*”被称为间接成员指针解引用操作符
#include <iostream>
#include <cstdio>
using namespace std;
class Student{
public:
Student(const string& name)
:m_name(name){}
int m_no;
int m_age;
string m_name;
};
int main(void)
{
string Student::*pname=&Student::m_name;
Student s1("杨健");
Student* s2 = new Student("王建立");
cout << s1.*pname << endl;
cout << s2->*pname << endl;
delete s2;
s2 = NULL;
//成员变量地址=
//对象地址+成员变量指针保存的相对地址
printf("pname=%p\n",pname);
printf("&s1.m_name=%p\n",&s1.m_name);
printf("&s1=%p\n",&s1);
return 0;
}
2.成员函数指针
1)定义
返回类型 (类名::*成员函数指针)(形参表) = &类名::成员函数名;
2)使用
(对象.*成员函数指针)(实参表);
(对象指针->*成员函数指针)(实参表);
参考代码:
#include <iostream>
#include <cstdio>
using namespace std;
class Student{
public:
Student(const string& name)
:m_name(name){}
void who(void){
cout << "我叫" << m_name << endl;
}
string m_name;
};
int main(void)
{
void (Student::*pwho)() = &Student::who;
Student s1("杨健");
Student* s2 = new Student("王建立");
(s1.*pwho)();
(s2->*pwho)();
return 0;
}