1.第一个是昨天的作业: 用类实现一个双向循环链表, 完成对应的功能,(增删查改 )
代码:
list.h
#ifndef LIST_H
#define LIST_H
#include
#include
using namespace std;
#define Type int //宏定义
typedef unsigned int u32;//无符号整型
/***** 节点的结构体 ****/
struct Node
{
Node():data(new Type),prev(NULL),next(NULL)
{
memset(data,0,sizeof(Type)); /*清零数据*/
}/*构造函数:申请空间*/
~Node(){delete data;}/*析构函数:释放空间*/
Type *data; /*数据域*/
Node *prev; /*前驱*/
Node *next; /*后继*/
};
/****** 链表的结构体 ******/
class List
{
public:
List() /*链表构造函数*/
{
/*1.申请空间*/
m_head = new Node;
/*2.更改指向*/
m_head->next = m_head;
m_head->prev = m_head;
/*3.初始化元素个数*/
m_size = 0;
}
// List(const List &list) /*链表拷贝构造*/
// :List()/*1.调用无参构造*/
// {
// /*2.拷贝list表的数据到this表中*/
// /*调用链表尾部增加 push_back()*/
// }
~List()
{
clear(); /*清空非表头节点*/
delete m_head;/*释放表头*/
}
public: /* 公有接口 */
bool push_back(const Type &value)/*链表尾部增加*/
{
return insert(m_head,value);
// /*1.新增节点*/
// Node *node = new Node;
// /*2.拷贝数据到新节点空间*/
// *node->data = value;
// /*3.找到要插入位置的前后节点*/
// Node *prev = m_head->prev;
// Node *next = m_head;
// /*4.更改指向*/
// node->prev = prev;
// node->next = next;
// prev->next = node;
// next->prev = node;
// /*5.元素个数+1*/
// m_size++;
// return true;
}
bool push_front(const Type &value)/*链表头部增加*/
{
return insert(m_head->next,value);
}
bool insert(Node *insert_node,const Type &value)/*链表任意节点位置插入*/
{
/*1.新增节点*/
Node *node = new Node;
/*2.拷贝数据到新节点空间*/
*node->data = value;
/*3.找到要插入位置的前后节点*/
Node *prev = insert_node->prev;
Node *next = insert_node;
/*4.更改指向*/
node->prev = prev;
node->next = next;
prev->next = node;
next->prev = node;
/*5.元素个数+1*/
m_size++;
return true;
}
bool insert(u32 index,Type &value)/*链表任意插入*/
{
if(index > m_size) index = m_size;
/**** 找到要插入位置的节点 ****/
Node *node = m_head->next;
for(u32 i = 0; i < index;i++)
{
node = node->next;
}
/**** 插入数据 ****/
return insert(node,value);
}
bool pop_back()/*链表尾部删除*/
{
if(m_size == 0) return false;
return erase(m_head->prev);
}
bool pop_front()/*链表头部删除*/
{
if(m_size == 0) return false;
return erase(m_head->next);
}
bool erase(int index)/*链表任意删除节点*/
{
if(m_size == 0 || index >= m_size) return false;
/*** 1.找到要删除的节点 ****/
Node *node = m_head->next;
for(int i = 0; i < index;i++)
{
node = node->next;
}
/*** 2.删除节点 ****/
return erase(node);
}
bool erase(Node *node)
{
/*** 1.找到前驱和后继 ***/
Node *prev = node->prev;
Node *next = node->next;
/*** 2.释放节点 *****/
delete node;
/*** 3.修改指向 ****/
prev->next = next;
next->prev = prev;
/*** 4.元素个数-1 **/
m_size--;
return true;
}
void clear()/*清空链表*/
{
while(m_size != 0)
{
pop_back();/*尾部删除*/
}
}
bool remove(const Type &value)/*删除第一个匹配的值 : 会调用 == 运算符重载*/
{
/*** 1.找到匹配节点 ****/
Node *node = m_head->next;
while(node != m_head)
{
if(*node->data == value)
{
/**** 删除该节点 ****/
return erase(node);
}
node = node->next; /*往后遍历*/
}
return false;
}
template /*虚拟类型*/
bool remove_if(_Predicate Predicate) /*删除第一个匹配的值 : 一元谓词: bool func(Type value) */
{
/*** 1.找到匹配节点 ****/
Node *node = m_head->next;
while(node != m_head)
{
if( Predicate(*node->data) == true)
{
/**** 删除该节点 ****/
return erase(node);
}
node = node->next; /*往后遍历*/
}
return false;
}
void remove_all(const Type &value)/*删除所有匹配的值: 会调用 == 运算符重载*/
{
/**** 1.找到匹配节点 ****/
while(remove(value) == true);
}
template /*虚拟类型*/
void remove_all_if(_Predicate Predicate)/*删除所有匹配的值: 一元谓词: bool func(Type value)*/
{
/**** 1.找到匹配节点 ****/
while(remove_if(Predicate) == true);
}
Type &front()/*返回链表第一个元素*/
{
return *m_head->next->data;
}
Type &back()/*返回链表最后一个元素*/
{
return *m_head->prev->data;
}
Type &operator [](u32 index) const/*提供随机访问*/
{
if(index >= m_size)
{
cout << "链表没有元素个数,爆出异常" << endl;
throw "异常:链表没有元素个数"; /*报异常*/
}
Node *node = m_head->next; /*第一个元素*/
for(u32 i = 0; i < index;i++)
{
node = node->next; /*往后遍历*/
}
return *node->data;
}
u32 size() const /*获取元素的个数*/
{
return m_size;
}
Type &find(Type value)
{
/*** 1.找到匹配节点 ****/
Node *node = m_head->next;
while(node != m_head)
{
if(*node->data == value)
{
/**** 返回该节点数据 ****/
break;
}
node = node->next; /*往后遍历*/
}
return *node->data;
}
template /*虚拟类型*/
Type &find_if(_Predicate Predicate)
{
/*** 1.找到匹配节点 ****/
Node *node = m_head->next;
while(node != m_head)
{
if(Predicate(*node->data))
{
/**** 返回该节点数据 ****/
break;
}
node = node->next; /*往后遍历*/
}
return *node->data;
}
static void sort(int *addr,int len) /*冒泡排序:调用 > 运算符重载 */
{
// int addr[];
// int len = 0;
bool flag = false;/*记录是否排好序,排好序:false,未排好序:true*/
int temp = 0;/*临时变量,用于交换*/
for(int i = 0; i < len - 1;i++)
{
flag = false;/*清空数据,用于下次记录*/
for(int j = 0; j < len - i - 1;j++)
{
if(addr[j] > addr[j+1])
{
flag = true;/*表名没有排好序*/
/*交换*/
temp = addr[j];
addr[j] = addr[j+1];
addr[j+1] = temp;
}
}
if(flag == false) break; /*检测是否排好序,排好需就直接退出*/
}
}
void sort()
{
bool flag = false;/*记录是否排好序,排好序:false,未排好序:true*/
Type *temp = NULL;/*临时指针,用于交换*/
Node *node_i = NULL;
Node *node_j = NULL;
/****** 排序 *******/
for(node_i = m_head->next ; node_i != m_head->prev ; node_i = node_i->next)
{
flag = false;/*清空数据,用于下次记录*/
for(node_j = m_head->next;node_j != m_head->prev ; node_j = node_j->next)
{
if(*node_j->data > *node_j->next->data)
{
flag = true;/*表名没有排好序*/
/*交换*/
temp = node_j->data;
node_j->data = node_j->next->data;
node_j->next->data = temp;
}
}
if(flag == false) break; /*检测是否排好序,排好需就直接退出*/
}
}
/***** 接口使用 *****/
protected:
Node *m_head; /* 节点:表头 */
u32 m_size; /* 元素个数 */
};
#endif // LIST_H
main.cpp
#include
#include "list.h"
using namespace std;
void Print(const List &list);
int main()
{
List list;
for(int i = 0 ; i < 10;i++)
{
list.push_front(i);
}
cout << "排序前:";
for(u32 i = 0; i < list.size();i++)
{
cout << list[i] << ",";
}
cout << endl;
list.sort();
cout << "排序后:";
for(u32 i = 0; i < list.size();i++)
{
cout << list[i] << ",";
}
cout << endl;
return 0;
}
int test2()
{
int addr[10] = {123,1234,236,437,64,51,6,6756,845,85};
int len = 10;
cout << "未排序addr : ";
for(int i = 0; i < len;i++)
{
cout << addr[i] << ",";
}
cout << endl;
List::sort(addr,len);
cout << "已排序addr : ";
for(int i = 0; i < len;i++)
{
cout << addr[i] << ",";
}
cout << endl;
}
int test1()
{
List list;
for(int i = 0; i < 10; i++)
{
//list.push_back(i); /*尾部插入:0,1,2,3,4,5,6,7,8,9*/
//list.insert(0,i);/*头部插入*/
list.insert(list.size(),i);/*尾部插入*/
}
cout << "第一个元素:" << list.front() << endl;
cout << "最后的元素:" << list.back() << endl;
/**** 删除头尾 ****/
list.pop_back();
list.pop_front();
cout << "输出list结构的数据:" << endl;
Print(list);
/**** 删除下标位 3的位置 *****/
list.erase(3);
cout << "输出list结构的数据:" << endl;
/**** 清空 ****/
//list.clear();
//list.remove(6);
list.push_back(5);
list.push_front(5);
//list.remove_all(5);
Print(list);
cout << "调用带 自定义策略的函数" << endl;
bool int_cmp(Type value); /*函数类声明函数*/
list.remove_all_if(int_cmp);
Print(list);
cout << "查询 3" << endl;
int value = list.find(10);
cout << "value = " << value << endl;
cout << "查询大于 5" << endl;
value = list.find_if([/*依赖*/](Type value/*参数列表*/)->bool/*返回值类型*/{
/*匿名函数的函数体*/
if(value > 5) return true;
return false;
});
cout << "value = " << value << endl;
cout << "程序正常结束" << endl;
return 0;
}
bool int_cmp(Type value)
{
if(value > 5) return true;
return false;
}
void Print(const List &list)
{
for(u32 i = 0; i < list.size();i++)
{
cout << list[i] << ",";
}
cout << endl;
}
2.继承,
/********* 多继承会造成的问题 *************************
* 1.父类成员同名问题?
* 答:使用类域调用解决
* 2.菱形继承(砖石继承)?:
* 人类
* 学生 工人
* 实习生
* 答:使用虚继承方式,形成续表索引(32位机:虚指针占4字节)
* *************************************************/
父类成员同名问题?
* 答:使用类域调用解决
代码:
#include
using namespace std;
class Student /*学生*/
{
public:
string m_name; /*姓名*/
int m_age; /*年龄*/
float m_score = 0; /*分数*/
public:
void study(){cout << "学习" << endl;}
void exam(){ cout << "考试" << endl;}
};
class Worker /*工人*/
{
public:
string m_name; /*姓名*/
int m_age; /*年龄*/
int m_salary = 0; /*薪水*/
public:
void wage(int hour)
{
int salary = 20 * hour;
m_salary += salary;
cout << "工作:" << hour << "小时,赚取:" << salary << "薪水" << endl;
}
};
/* 实习生,继承于 学生 和 工人 */
class Trainee : public Student , public Worker
{
public:
void sleep(){ cout << "我是实习生" << endl;}
};
/********* 多继承会造成的问题 *************************
* 1.父类成员同名问题?
* 答:使用类域调用解决
* 2.菱形继承(砖石继承)?:
* 人类
* 学生 工人
* 实习生
* 答:使用虚继承方式,形成续表索引(32位机:虚指针占4字节)
* *************************************************/
int main()
{
Trainee trainee;
cout << "m_score = " << trainee.m_score << endl;
cout << "m_salary = " << trainee.m_salary << endl;
/******** 使用成员同名变量 ********/
trainee.Student::m_name = "王老师";
trainee.Worker::m_name = "王无敌";
cout << "name = " << trainee.Student::m_name << endl;
cout << "name = " << trainee.Worker::m_name << endl;
return 0;
}
虚继承方式,形成续表索引
#include
using namespace std;
class People
{
public:
string m_name; /*姓名*/
int m_age; /*年龄*/
};
class Student : virtual public People /*学生*/
{
public:
float m_score = 0; /*分数*/
public:
void study(){cout << "学习" << endl;}
void exam(){ cout << "考试" << endl;}
};
class Worker : virtual public People /*工人*/
{
public:
int m_salary = 0; /*薪水*/
public:
void wage(int hour)
{
int salary = 20 * hour;
m_salary += salary;
cout << "工作:" << hour << "小时,赚取:" << salary << "薪水" << endl;
}
};
/* 实习生,继承于 学生 和 工人 */
class Trainee : public Student , public Worker
{
public:
void sleep(){ cout << "我是实习生" << endl;}
};
/********* 多继承会造成的问题 *************************
* 1.父类成员同名问题?
* 答:使用类域调用解决
* 2.菱形继承(砖石继承)?:
* 人类
* 学生 工人 -> 这里使用虚继承 virtual public People
* 实习生
* 答:使用 virtual 虚继承方式,形成续表索引(32位机:虚指针占4字节)
* 3.虚继承的好处
* *************************************************/
int main()
{
#if 0
/**** 探讨大小 ***/
cout << "sizeof(string) = " << sizeof(string) << endl; //24字节
cout << "sizeof(People) = " << sizeof(People) << endl; //28字节
cout << "sizeof(Student)= " << sizeof(Student) << endl; //32字节
cout << "sizeof(Worker) = " << sizeof(Worker) << endl; //32字节
cout << "sizeof(Trainee)= " << sizeof(Trainee) << endl; //64字节
#else
/*—— 通过以上可以看出,我共同继承于人类部分多余了,只用一份空间就行,所以照成空间浪费——*/
/*答:使用 virtual 虚继承方式解决多该案例多占用 28 字节,但是 virtual是虚指针所以需要占用 4字节空间*/
/**** 探讨大小 ***/
cout << "sizeof(string) = " << sizeof(string) << endl; //24字节
cout << "sizeof(People) = " << sizeof(People) << endl; //28字节
cout << "sizeof(Student)= " << sizeof(Student) << endl; //32+4字节
cout << "sizeof(Worker) = " << sizeof(Worker) << endl; //32+4字节
cout << "sizeof(Trainee)= " << sizeof(Trainee) << endl; //64+8-28字节
/*———————————— 通过以上案例,得出已经只占用一片空间 ————————————*/
#endif
Trainee trainee;
trainee.m_name = "老师";
cout << "姓名:" << trainee.People::m_name << endl;
cout << "姓名:" << trainee.Student::m_name << endl;
cout << "姓名:" << trainee.Worker::m_name << endl;
cout << "姓名:" << trainee.Trainee::m_name << endl;
return 0;
}
3.多态 ()
多态的定义:
多态是C++面向对象三大特性之一 多态分为两类: ◼静态多态: 函数重载 和 运算符重载属于静态多态,复用函数名 ◼动态多态: 派生类和虚函数实现运行时多态(重写)
静态多态和动态多态区别: ◼静态多态的函数地址早绑定 - 编译阶段确定函数地址 ◼动态多态的函数地址晚绑定 - 运行阶段确定函数地址
总结: 多态满足条件: ◼有继承关系 ◼子类重写父类中的虚函数(”虚“会被继承) 多态使用条件: ◼父类指针或引用指向子类对象 重写:函数返回值类型 函数名 参数列表 完全一致称为重写
多态的优点
多态的优点: ◼代码组织结构清晰 ◼可读性强 ◼利于前期和后期的扩展以及维护 总结:C++开发提倡利用多态设计程序架构,因为多态优点很多
代码:(加强版)
#include
using namespace std;
class People
{
public:
/**** 虚函数 -> Qt里面 函数名变成斜体字 ****/
virtual void study()
{
cout << "自我摸索学习" << endl;
}
public:
string m_name;
int m_age;
};
class Student : public People
{
public:
void study() /* -> 重写 */
{
cout << "跟着大牛老师学习" << endl;
}
};
class Teacher : public People
{
public:
void study() /* -> 重写 */
{
cout << "看国际论坛博文学习" << endl;
}
};
class Librarian : public People
{
public:
void study() /* -> 重写 */
{
cout << "看图书学习" << endl;
}
};
class Programmer : public Student
{
public:
void study() /* -> 重写 */
{
cout << "百度CP复制学习" << endl;
}
};
#include /*链表 // STL容器*/
void Print_study(People &value1)
{
value1.study();/*打印学习方法*/
}
int main()
{
Student student;
Teacher teacher;
Programmer programmer;
Print_study(student);
Print_study(teacher);
Print_study(programmer);
#if 0
list list_v;
/******* 插入 *******/
list_v.push_back(new People);
list_v.push_back(new Student);
list_v.push_back(new Programmer);
list_v.push_back(new Librarian);
list_v.push_back(new Teacher);
/******* 遍历 *******/
list::iterator it;
for(it = list_v.begin();it != list_v.end();it++)
{ /** it 遍历出来的指针 **/
(*it)->study();
}
#endif
return 0;
}
代码(普通版)
#include
using namespace std;
/***** 饮品制作的基类 *****/
class Beverage
{
public:
virtual void boil() = 0; /*煮水*/
virtual void brewing() = 0; /*冲泡*/
virtual void with_cup() = 0; /*倒入杯中*/
virtual void ingredients() = 0; /*加入辅料*/
public:
void make()/*制作*/
{
boil();
brewing();
with_cup();
ingredients();
}
};
/***** 制作咖啡 ****/
class Coffee : public Beverage
{
public:
void boil()
{
cout << "煮矿泉水" << endl;
}
void brewing()
{
cout << "冲泡咖啡" << endl;
}
void with_cup()
{
cout << "倒入咖啡杯中" << endl;
}
void ingredients()
{
cout << "加入红糖和牛奶" << endl;
}
};
class Tea : public Beverage
{
public:
void boil()
{
cout << "煮山泉水" << endl;
}
void brewing()
{
cout << "冲泡茶叶" << endl;
}
void with_cup()
{
cout << "倒入茶杯中" << endl;
}
void ingredients()
{
cout << "加入枸杞,人生片" << endl;
}
};
void make(Beverage &beverage)
{
beverage.make();
}
int main()
{
Coffee coffee;
Tea tea;
make(coffee);
make(tea);
return 0;
}
4.多态的时候,开辟空间需要加上 (虚析构!!)
虚析构和纯虚析构
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码 解决方式:将父类中的析构函数改为虚析构或者纯虚析构
虚析构和纯虚析构共性: ◼可以解决父类指针释放子类对象 ◼都需要有具体的函数实现 虚析构和纯虚析构区别: ◼如果是纯虚析构,该类属于抽象类,无法实例化对象
虚析构语法:virtual ~类名(){} 纯虚析构语法:virtual ~类名() = 0;
总结: ◼ 虚析构或纯虚析构就是用来解决通过父类指针释放子类对象 ◼ 如果子类中没有堆区数据,可以不写为虚析构或纯虚析构 ◼ 拥有纯虚析构函数的类也属于抽象类
代码:
#include
using namespace std;
class A
{
public:
A(){cout << __func__ << endl;}
virtual ~A(){cout << __func__ << endl;} //这里可以尝试把 virtual 去掉 调试试试
};
class B :public A
{
public:
B(){cout << __func__ << endl;}
~B(){cout << __func__ << endl;}
};
/**** 一般在调用多态时会造成内存泄漏,可以使用虚析构或纯虚析构解决实现 ****/
int main()
{
A *a = new B; /* A() */
delete a; /* ~A() */
return 0;
}
5.文件操作
C++中对文件操作需要包含头文件 < fstream >
文件类型分为两种:
操作文件的三大类:
10.1文本文件
10.1.1写文件
写文件步骤如下:
包含头文件
#include
创建流对象
ofstream ofs;
打开文件
ofs.open(“文件路径”,打开方式);
写数据
ofs << “写入的数据”;
关闭文件
ofs.close();
代码:
#include
#include /* 文件流类 */
using namespace std;
class Student
{
public:
Student(string name = "",int age = 0):m_name(name),m_age(age){}
string m_name;
int m_age;
};
/********** 操作集 ***************************************************************************
* 打开:
* void open(const char* __s, ios_base::openmode __mode = ios_base::in | ios_base::out)
* void open(const std::string& __s,ios_base::openmode __mode = ios_base::in | ios_base::out)
* 参数:
* @char* __s: C语言风格文件名
* @string s : C++风格文件名
* @ios_base::openmode mode : 打开模式
* ios_base::in 写模式
* ios_base::out 读模式
* ios_base::trunc 创建
* ios_base::binary 二进制
* ios_base::app 追加
*————————————————————————————————————————————————————————————————————————————————————————————
* 关闭:
* void close()
*————————————————————————————————————————————————————————————————————————————————————————————
* 判断是否打开成功:
* bool is_open() const
* 返回值:
* 成功: true
* 失败: false
*——————————————————————————————————————————————————————————————————————————————————————————————
*格式化读取:
* operator >>
*格式化写入:
* operator <<
*——————————————————————————————————————————————————————————————————————————————————————————————
*块写入:
* write(const char_type* __s, streamsize __n)
*块读取:
* read(char_type* __s, streamsize __n)
*——————————————————————————————————————————————————————————————————————————————————————————————
*字节写入:
* put(char_type __c)
*字节读取:
* int_type get()
*———————————————————————————————————————————————————————————————————————————————————————————————
*读取一行:
* getline(char_type* __s, streamsize __n)
* *************************************************************************************************/
#include
int main()
{
/* 1.实例化对象 */
fstream fd;
/* 2.打开文件 */
#if 0
fd.open("test.txt",ios_base::trunc | ios_base::in | ios_base::out); /*清空写*/
#else
fd.open("test.txt",ios_base::in | ios_base::out); /*读写操作*/
#endif
/* 3.判断是否打开成功 */
if(fd.is_open() == false)
{
cout << "文件打开失败:文件不存在" << endl;
return -1;
}
/* 4.读写操作 */
#if 0
#else
char buf[128] = "";
fd.getline(buf,128);
cout << "buf = " << buf << endl;
#endif
/* 5.关闭文件 */
fd.close();
return 0;
}
int test3()
{
/* 1.实例化对象 */
fstream fd;
/* 2.打开文件 */
#if 0
fd.open("test.txt",ios_base::trunc | ios_base::in | ios_base::out); /*清空写*/
#else
fd.open("test.txt",ios_base::in | ios_base::out); /*读写操作*/
#endif
/* 3.判断是否打开成功 */
if(fd.is_open() == false)
{
cout << "文件打开失败:文件不存在" << endl;
return -1;
}
/* 4.读写操作 */
#if 0
char buf[128] = "老师xxx";
for(int i = 0; i < strlen(buf);i++)
{
fd.put(buf[i]);
}
#else
char ch = 0;
while(fd.eof() == false)
{
ch = fd.get();
cout << ch;
}
cout << endl;
#endif
/* 5.关闭文件 */
fd.close();
return 0;
}
int test2()
{
/* 1.实例化对象 */
fstream fd;
/* 2.打开文件 */
#if 0
fd.open("test.txt",ios_base::trunc | ios_base::in | ios_base::out); /*清空写*/
#else
fd.open("test.txt",ios_base::in | ios_base::out); /*读写操作*/
#endif
/* 3.判断是否打开成功 */
if(fd.is_open() == false)
{
cout << "文件打开失败:文件不存在" << endl;
return -1;
}
/* 4.读写操作 */
#if 0
char buf[128] = "老师,无敌";
fd.write(buf,128); /*写入一块*/
#else
char buf[128] = "";
fd.read(buf,128);
cout << "buf = " << buf << endl;
#endif
/* 5.关闭文件 */
fd.close();
return 0;
}
int test0()
{
/* 1.实例化对象 */
fstream fd;
/* 2.打开文件 */
#if 0
fd.open("test.txt",ios_base::trunc | ios_base::in | ios_base::out); /*清空写*/
#else
fd.open("test.txt",ios_base::in | ios_base::out); /*读写操作*/
#endif
/* 3.判断是否打开成功 */
if(fd.is_open() == false)
{
cout << "文件打开失败:文件不存在" << endl;
return -1;
}
/* 4.读写操作 */
#if 0
Student stu1("王老师",18);
Student stu2("李老师",19);
fd << stu1.m_name << " " << stu1.m_age << endl;
fd << stu2.m_name << " " << stu2.m_age << endl;
#else
while(fd.eof() == false)
{
Student stu1;
fd >> stu1.m_name >> stu1.m_age;
if(stu1.m_age != 0)
cout << "姓名:" << stu1.m_name << "\t年龄:" << stu1.m_age << endl;
}
#endif
/* 5.关闭文件 */
fd.close();
return 0;
}