Review cpp day06

Posted 达少Rising

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Review cpp day06相关的知识,希望对你有一定的参考价值。

回顾:
Review cpp day01
Review cpp day02
Review cpp day03
Review cpp day04
Review cpp day05

十七、拷贝构造和拷贝赋值

1、浅拷贝和深拷贝

  • 1)如果一个类中包含指针形式的成员变量,缺省的拷贝构造函数只是复制指针变量本身,而没有复制指针所指向的内容,这中拷贝方式称为浅拷贝。
  • 2)浅拷贝将会导致不同对象间的数据共享,同时会在析构函数中引发double free异常,因此就必须自己定义一个支持复制指针所指向内容拷贝构造函数,即深拷贝。
    在这里插入图片描述

2、拷贝赋值

  • 1)当编译器看到两个对象的赋值操作时,比如“i3=i2”,会将其翻译成函数调用的形式:i3.operator=(i2),该函数称为拷贝赋值操作符函数,由该函数完成两个对象的复制过程。
  • 2)但是缺省的拷贝赋值函数和缺省拷贝构造函数类似,也是浅拷贝,有double free和内存泄露问题。
  • 3)所以必须自己定义支持深拷贝的拷贝赋值操作符函数:
	类名& operator=(const 类名& that){
		if(&that != this){//防止自赋值
			//释放旧资源
			//分配内存资源
			//拷贝新数据
		}
		return *this;//返回自引用
	}

01copy.cpp

#include <iostream>
using namespace std;

class Integer{
public:
	Integer(int data = 0):m_data(new int(data)){
		//m_data = new int(data);
	}
	void print(void)const{
		cout << *m_data << endl;
	}
	~Integer(void){
		cout << "析构函数" << endl;
		delete m_data;
	}
	//Integer i2(i1)
	//i2.m_data = i1.m_data;
	//缺省的拷贝构造函数(浅拷贝)
	/*Integer(const Integer& that){
		m_data = that.m_data;
	}*/

	//自定义深拷贝构造
	Integer(const Integer& that){
		m_data = new int(*that.m_data);
	}

	/*
	//i3 = i2;==>i3.operator=(i2)
	//i3.m_data = i2.m_data;
	//类中缺省的拷贝赋值函数
	Integer& operator=(const Integer& that){
		cout << "拷贝赋值操作符函数"
		m_data = that.m_data;
		return *this;
	}
	*/

	//i3 = i2;==>i3.operator=(i2)
	//*i3.m_data = *i2.m_data;
	//自定义深拷贝赋值函数
	Integer& operator=(const Integer& that){
		if(&that != this){
			delete m_data;
			m_data = new int;
			*m_data = *that.m_data;
		}
		return *this;
	}
private:
	int* m_data;
};
int main(void){
	Integer i1(100);
	Integer i2(i1);//拷贝构造
	
	i1.print();//100
	i2.print();//100

	Integer i3;
	//i3.operator=(i2);
	i3 = i2;//拷贝赋值
	return 0;
}
  • 笔试题/练习:实现String类
class String{
public:
	//构造函数
	String(const char* str){
		m_str = new char[strlen(str) + 1];
		strcpy(m_str, str);
	}
	//析构函数
	//拷贝构造函数
private:
	char* m_str;
public:
	const char* c_str()const{
		return m_str;
	}
};
int main(){
	String s1("hello world!");
	cout << s1.c_str() << endl;
	String s2 = s1;
	cout << s2.c_str() << endl;
	return 0;
}

02string.cpp(重点代码)

#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);
		}
		return *this;//返回自引用
	}
private:
	char* m_str;
public:
	const char* c_str()const{
		return m_str;
	}
};
int main(void){
	String s1 = "hello";
	String s2 = s1;
	cout << s1.c_str() << endl;
	cout << s2.c_str() << endl;
	
	String s3 = "hello world!";
	//s2.operator = (s3);
	s2 = s3;//拷贝赋值
	cout << s2.c_str() << endl;
	return 0;
}

十八、静态成员(static)

1、静态成员变量

  • 1)普通的成员属于对象,而静态成员变量不属于对象。
  • 2)不能在构造函数中定义和初始化,需要在类的外部单独定义和初始化。
  • 3)静态成员变量和全局变量一样存放在全局区,可以把静态成员变量理
    解成是被限制在类中去使用的全局变量。
  • 4)使用方法:
    • a)类名::静态成员变量名;//推荐
      A::s_data;

    • b)对象名.静态成员变量名;//和上面方法本质一样

    A a1(20);
    a1.s_data;
    
class 类名{
	static 数据类型 变量名;//声明
};
数据类型 类名::变量名 = 初值;//定义

03static.cpp

#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 = 10;
int main(void){
	A a1(20);
	cout << "size=" << sizeof(a1) << endl;//size=4

	cout << A::s_data << endl;//10
	cout << a1.s_data << endl;//10
	
	cout << a1.m_data << endl;//20

	A a2;
	//静态成员变量在该类的对各对象间共享
	a2.s_data = 123;
	//普通成员变量在不同对象中相互独立
	a2.m_data = 321;
	
	cout << a1.s_data << endl;//123
	cout << a1.m_data << endl;//20
	return 0;
}

2、静态成员函数

  • 语法:
    class 类名{
    	static 返回类型 函数名(形参表){...}
    };
    
  • 1)静态成员函数没有this指针,没有const属性
  • 2)使用方法:
    • 类名::静态成员函数(实参表);//推荐
    • 对象名.静态成员函数(实参表);//和上面方法本质一样

注: 在静态成员函数中只能访问静态成员,不能访问普通成员,因为静态成员函数中没有this指针;在普通成员函数中既可以访问静态成员也可以访问普通成员

04static.cpp

#include <iostream>
using namespace std;

class A{
public:
	//普通成员变量在构造函数初始化
	A(int data):m_data(data){}	
	void func1(void){
		cout << "非静态成员函数" << endl;
	}
	static void func2(void){
		cout << "静态成员函数" << endl;
		cout << s_data <<endl;//10
		//静态函数中只能访问静态成员,不能访问非静态成员,因为静态函数中没有this指针
		//cout << m_data << endl;//error
	}
	int m_data;
	static int s_data;//声明
};
//静态成员变量需要在类的外部单独定义和初始化
int A::s_data = 10;
int main(void){
	//静态成员函数可以通过类名::直接调用
	A::func2();
	A a;
	//普通成员函数必须通过对象调用
	a.func1();//A::func1(&a)
	//静态成员函数也可以通过对象去调用
	a.func2()//A::func2()
	return 0;
}

3、单例模式

  • 定义:一个类只允许创建唯一的对象,并提供它的访问方法。

  • 1)禁止在类的外部创建对象:私有化构造函数;

  • 2)类的内部维护唯一的对象:静态成员变量;

  • 3)提供单例对象的访问方法:静态成员函数;

  • 4)创建方式:

    • 饿汉式
    • 懒汉式
  • 饿汉式:无论用或不用,程序启动即创建(即在main函数之前已经创建)

05hungry.cpp

//单例模式:饿汉式
#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=0):m_data(data){
		cout << "单例对象被创建了"  << endl;
	}
	Singleton(const Singleton&);

	//2)通过静态成员变量维护唯一的对象
	static Singleton s_instance;
private:
	int m_data;
};
Singleton Singleton::s_instance(12345);
int main(void){
	cout << "main开始执行" << endl;
	//Singleton s1;//error
	//Singleton* ps = new Singleton();//error
	Singleton& s1 = Singleton::getInstance();
	Singleton& s2 = Singleton::getInstance();
	cout << "&s1=" << &s1 << endl;//地址是一样的
	cout << "&s2=" << &s2 << endl;
	s1.print();//12345
	s2.print();//12345
	return 0;
}
单例对象被创建了
main开始执行
&s1=0xxxxxxxxx
&s2=0xxxxxxxxx
12345
12345
  • 懒汉式:用时再创建,不用立即销毁(创建在main函数之后)

06lazy.cpp

//单例模式:懒汉式
#include <iostream>
using namespace std;

class Singleton{
public:
	//3)通过静态成员函数获取单例对象
	static Singleton& getInstance(void){
		if(s_instance == NULL){
			s_instance = new Singleton(54321);
		}
		++s_count;
		return *s_instance;
	}
	//单例对象可能被对个线程使用,应该是由最后一个线程释放
	void release(void){
		if(--s_count == 0){
			delete s_instance;
		}
	}
	void print(void)const{
		cout << m_data << endl;
	}
private:
	//1)私有化构造函数
	Singleton(int data=0):m_data(data){
		cout << "单例对象被创建了"  << endl;
	}
	Singleton(const Singleton&);
	~Singleton(void){
		cout << "单例对象已销毁了" << endl;
	}
	//2)通过静态成员变量维护唯一的对象
	static Singleton* s_instance;
	//计数:记录使用单例对象的个数
	static int s_count;
private:
	int m_data;
};
Singleton* Singleton::s_instance = NULL;
int Singleton::s_count = 0;
int main(void){
	cout << "main开始执行" << endl;
	//Singleton s1;//error
	//Singleton* ps = new Singleton();//error
	Singleton& s1 = Singleton::getInstance();
	Singleton& s2 = Singleton::getInstance();
	cout << "&s1=" << &s1 << endl;//地址是一样的
	cout << "&s2=" << &s2 << endl;
	s1.print();//12345
	s2.print();//12345
	s1.release();
	s3.release();
	return 0;
}
main开始执行
单例对象被创建了
&s1=0xxxxxxxxx
&s2=0xxxxxxxxx
12345
12345
单例对象已销毁了
  • 扩展练习V4.0:实现企业员工管理系统
    • 需求:继续优化和修改员工类
      1)禁止拷贝和拷贝复制
      2)增加静态成员变量,记录员工人数,并写入文件(count.txt)
      3)增加静态成员函数,获取员工人数
      4)再创建员工对象,从指定文件(id.txt)获取工号,每次获取工号后加1,保证每个员工的工号不能重复。

Employee.h

#include <iostream>
#include <cstdio>
using namespace std;

class Employee{
public:
	Employee(const string& name, double salary);
	~Employee(void);
	void printInfo(void)const;
	void claSalary(void);
	void setId(int id);
	void setName(const string& name);
	void setSalary(double salary);
	void saveInfo(void)const;
private:
	//禁止拷贝构造函数和拷贝复制
	Employee& operator=(const Employee&);
	Employee(const Employee&);
private:
	int m_id;//工号
	string m_name;//姓名
	double m_salary;//工资
	FILE* file;
public:
	//获取员工人数
	static const int& getEmployeeCount(void);
private:
	static int m_count;//记录员工人数
};

Employee.cpp

#include "Employee.h"
Employee::Employee(const string& name, double salary)
		:m_name(name), m_saraly(salary){
	//读取ID
	FILE* fp = fopen("id.txt", "r+");
	fscanf(fp, "%d", &m_id);
	//将文件读写指针定位到文件头
	fseek(fp, 0, SEEL_SET);
	//ID+1, 再保存回id.txt
	fprintf(fp, "%d", m_id+1);
	//关闭id.txt
	fclose(fp);

	//将ID转换为字符串
	char filename[20] = {0};
	sprintf(filename, "%d", m_d);
	//根据id创建文件保存当前员工信息
	file = fopen(filename, "w");
	//保存员工信息
	saveInfo();
	
	//记录员工人数
	++m_count;
	//保存与纳贡人数
	FILE* fp2 = fopen("count.txt", "w");
	fprintf(fp2, "%d", m_count);
	fclose(fp2);
}
Employee::~Employee(void){
	fclose(file);
	file =NULL;
}
Employee::Employee(const Employee& that){
	char filename[20] = {0};
	sprintf(filename, "%d", that.m_id);
	file = fopen(filename, "r+");
	m_id = that.m_id;
	m_name = that.m_name;
	m_salary = that.m_salary;
}
void Employee::printInfo(void)const{
	cout << "姓名:" << m_name << endl;
	cout << "工号:" << m_id << endl;
	cout << "基础工资:" << m_salary << endl; 
}
void Eployee::calSalary(void){
	cout << "请输入出勤天数:";
	int days;
	cin >> days;
	double basic = m_salary * (days/23.0);
	double merit = basic / 2;
	cout << "总工资为:" << (basic + merit) << endl;
}
void Employee::setid(int)以上是关于Review cpp day06的主要内容,如果未能解决你的问题,请参考以下文章

Review cpp day09

Review cpp day07

Review cpp day08

Review cpp day01

Review cpp day04

Review cpp day03