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的主要内容,如果未能解决你的问题,请参考以下文章