1、类成员属性在不同位置的不同权限:
权限|位置 类外 类内 派生(继承的子类)
公有: √ √ √
保护: × √ √
私有: × √ ×
总结:
类外:只能访问公有成员
类内:可访问所有成员
派生:无法访问祖先类的私有成员
class People
{
/* 保护或者私有成员属性提供安全接口 */
public:
string Name()const {
return this->m_name;
}
void SetName(string name){
this->m_name = name;
}
string Sex()const {
return this->m_sex;
}
void SetSex(string sex) {
if(sex == "男" || sex == "女")/*保护数据安全性*/
this->m_sex = sex;
}
int Age()const {
return this->m_age;
}
void SetAge(int age) {
this->m_age = age;
}
protected:/*保护:派生类可继承,外部无法访问*/
int m_sex;
string m_name;
int m_age;
};
2、子类可继承父类公有和保护的成员属性
公有: 一般想让外界直接访问的之后使用公有权限, 类似接口函数
保护: 既不想要外部访问,但是又想让派生类继承使用则使用保护权限
私有: 只允许内部自己访问,派生无法访问
#include <iostream>
#include <string>
using namespace std;
class Parent
{
protected:/*保护:派生类可继承,外部无法访问*/
string m_sex;
string m_name;
int m_age;
};
class Child :public Parent
{
public:
/*子类继承父类公有和保护的成员属性*/
Child()
{
this->m_sex = "女";
this->m_age = 19;
this->m_name = "李华";
this->schoolId = 2410000008;
}
/*不能在类外访问,需要调用接口函数打印*/
void Print()
{
cout<<this->m_name<<" "<<this->m_age
<<" "<<this->m_sex<<" "<<this->schoolId<<endl;
}
private:
long long schoolId;
};
int main()
{
Child child1;
child1.Print();
return 0;
}
3、构造函数和析构函数
3.1构造函数:
作用:用于申请空间时初始化处理
定义:
1.函数名与类名相同
2.无返回值
--->通过从上定义规则可以发现,构造函数可以拥有参数,则可实现重载
3.2析构函数:
作用:用于销毁时做释放处理
定义:
1.函数名与类名相同,但多了一个~号
2.无参,无返回值
--->然通过以上定义规则发现,析构函数无法重载
3.3注意:
如果用户没有提供构造和析构以及拷贝等函数则编译器默认提供
1.无参构造
2.拷贝构造(浅)
3.析构函数
4. =运算符函数
class People
{
pubilc:
People(){}
};
People peo1;
People peo2(peo1); /*拷贝构造*/
People peo3 = peo1; /*拷贝构造*/
peo3 = peo2; /*=号赋值运算符*/
4、深浅拷贝
浅拷贝: 是值拷贝,浅拷贝中包含指针变量,则都是指向到同一片内存空间地址
会造成:①一个操作多个对象 ②多次析构照成程序崩溃
深拷贝: 在堆区申请空间的操作都要实现
注意:一般牵扯指针操作,都需要自己实现深拷贝
/*********补充*************/
5、初始化列表的使用:
使用 “ :”开始,多参数用逗号隔
三种权限均可使用初始化列表来初始化
①构造函数 用 初始化列表调用其他构造函数 -> 减少代码量
②继承中的初始化列表使用 继承来的父类成员属性初始化(使用父类的有参构造)
class People
{
public:
string m_name;
protected:
string m_sex;
private:
int m_age;
public:
People(string name,string sex,int age)
:m_name(name),m_sex(sex),m_age(age)
{
}
};
6、常量成员属性 常量成员函数 常对象的区别
6.1常量成员属性:
定义:在变量名之前加 const
作用:只初始化赋值一次,以后无法更改,必须由 "初始化列表" 操作
6.2常量成员函数:
定义:在函数参数后加 const
作用:该函数不可修改类成员属性值,只可访问 自己在函数中定义的变量可以修改
注意:一般读取函数都会写成const常函数
6.3常对象:只能访问常函数,后面set容器存放的都会变成常对象
class People
{
public:
const int value;//常成员属性(变量)
int temp;
People():value(10)
//1、常成员属性只能初始化一次,以后无法更改 只能用“初始化列表”操作赋值
{
}
void Print()const
//2、常成员函数
{
//访问 可以
cout<<"value:"<<value<<endl;
cout<<"temp"<<temp<<endl;
//修改:不行
//temp = 90;
}
void SetValue(int v)
{
temp = v;
}
int getTemp() const;
void setTemp(int value);
};
int main(int argc, char *argv[])
{
People peo1;
const People peo2; // 3、常对象,只能访问常函数
peo1.Print(); //正常对象 调用 const 成员函数 √
peo1.SetValue(99); //正常对象 调用 非const 成员函数 √
peo2.Print(); //常对象 调用 const 成员函数 √
//peo2.SetValue(99); //常对象 调用 非const 成员函数 ×
return 0;
}
7、静态成员特征
7.1公有特征:
1.可以通过对象 或 类名访问
2.所有对象公用同一静态变量空间
7.2 静态成员属性:
类内声明,类外初始化
常用: 用作数据记录
须知:
1.所有对象共享同一份数据
2.空间在编译阶段就已经分配
3.静态成员不占用对象空间
7.3 静态成员函数:
类内声明类外实现也可以,类内实现也可以
注意:静态成员函数不能使用非静态成员,所以也不能访问this指针
常用:接口处理(数据集,固定参数"串口参数,摄像头参数,音频参数,网络参数等")
class People
{
public:
static int s_value; /*静态成员属性:类内声明,类外初始化*/
int m_value;
public:
People()
{
s_value++;
}
public:
static void GetValue() /* 静态成员函数 */
{
cout << "s_value = " << s_value << endl;
//cout << "m_value = " << m_value << endl; 静态成员函数不能使用非静态成员
}
static string GetIP()
{
return "IPV4 192.168.1.100";
}
static int GetMax(int value1,int value2)
{
return value1 > value2 ? value1 : value2;
}
};
/** 静态成员属性类外初始化 **/
int People::s_value = 0;
8、友元
分类:
①类做友元
//一、类做友元
class House
{
friend class Friend_v; /*1. 类 友元声明*/
public:
int m_zoulang = 1; //走廊
protected:
int m_keting = 2; //客厅
private:
int m_bedroom = 3; //卧室
};
class Friend_v
{
public:
/*******************方法①*******************/
House house1; /* 2.1 被友元的类 成员 当成 此类 成员 */
int get_Bedroom()const /* 2.2 调用this->被友元的类 成员.(私有|保护属性) */
{
return this->house1.m_bedroom;
}
/*******************方法②*******************/
static void visit(House h) /*3. 友元类的成员函数直接访问*/
{
cout<<"参观走廊"<<h.m_zoulang<<endl;
cout<<"参观客厅"<<h.m_keting<<endl;
cout<<"参观卧室"<<h.m_bedroom<<endl;
}
};
int main()
{
/* 方法① */
Friend_v fri1;
cout<<fri1.get_Bedroom()<<endl;
/* 方法② */
House hou1;
Friend_v::visit(hou1);
return 0;
}
②全局函数做友元
class House
{
friend void detection(House value); /* 1.全局友元函数 友元声明 */
public:
int m_zoulang = 1; //走廊
protected:
int m_keting = 2; //客厅
private:
int m_bedroom = 3; //卧室
};
void detection(House value)
{
cout << "走廊:" << value.m_zoulang << endl;
cout << "客厅:" << value.m_keting << endl;
cout << "卧室:" << value.m_bedroom << endl;
}
int main()
{
House hou1;
detection(hou1); /* 2.调用全局友元函数 */
return 0;
}
③类成员函数做友元
class House; /* 2.提前空声明被友元函数 */
class Friend_Func
{
public: /* 3.友元类的成员函数 在类内声明,类外实现(在被友元类 下面 类外实现)*/
void visit_friend_func(House value);
void not_friend(House value);
};
class House
{
friend void Friend_Func::visit_friend_func(House value);/* 1.类函数 友元声明 */
public:
int m_zoulang = 1;
protected:
int m_keting = 2;
private:
int m_bedroom = 3;
};
void Friend_Func::visit_friend_func(House value) /* 4.成员函数的实现 */
{
cout << "清洁走廊:" << value.m_zoulang << endl;
cout << "清洁客厅:" << value.m_keting << endl;
cout << "清洁卧室:" << value.m_bedroom << endl;
}
void not_friend(House value)
{
cout<<"未在被友元类中声明,不可访问保护|私有属性"<<endl;
}
int main()
{
House hou1;
Friend_Func fri_func;
fri_func.visit_friend_func(hou1);
return 0;
}
9、运算符重载
运算符可以被重载为全局函数(类外)、成员函数(类内)。一般倾向于重载为成员函数,这样能够较好地体现运算符和类的关系。
注意:不可重载的运算符:
. :成员访问运算符
.* 和 ->* :成员指针访问运算符
:: :域运算符
sizeof :长度运算符
? : :条件运算符
# : 预处理符号
(1) “+”运算符:
类外:实现的时候,必须(满参数),直接调用函数名
类内:类内实现,第一个参数不用填,使用this代替,直接调用函数名
class People
{
private:
int m_age;
public:
string m_name;
People(string name = "",int age = 0)
:m_name(name),m_age(age) {}
void Print()
{
cout<<this->m_name<<" "<<this->m_age<<endl;
}
People operator +(People v) /* 1.类内实现 */
{
People temp;
temp.m_name = this->m_name + v.m_name;
temp.m_age = this->m_age + v.m_age;
return temp;
}
};
People operator +(People v1,People v2) /* 2.类外实现 */
{
People temp;
temp.m_name = v2.m_name + v1.m_name;
//temp.m_age = v2.m_age + v1.m_age; /* 类外实现无法访问私有属性 */
return temp;
}
int main(int argc, char *argv[])
{
People peo1("李华",17);
People peo2("小美",23);
People peo3,peo4;
peo3 = peo1.operator +(peo2); /* (a + b)等价于 a.operator - (b) */
cout<<(peo1 + peo2).name<<endl;
peo3.Print();
peo4 = operator +(peo1,peo2); /* c = a + b; 等价于 c = operator + (a,b);*/
peo4.Print();
//peo4 = peo1+peo2; /* +运算符 类内、外同时实现,不能直接使用 a + b形式,需使用函数名 */
return 0;
}
(2)“++”自加运算符:(单目运算符 分 前置和后置 (后置要加int占位))
①int类型前置++规则如下:
int a; ++(++a) int类型的变量可以连续前置自加
cout<<++(++a)<<" "<<a<<endl; ++(++a) a 两值相等
返回值应该是引用,不然返回值是临时变量,自加后成员属性的值并没有改变
People &operator ++() /* 类内实现 */
{
this->m_age += 1;
return *this;
}
②后置++:(括号内加int占位)
int a; (a++)++ ×不允许连续后置自加,且自加后为只读值,函数应该为常量函数
cout<<(a++)<<" "<<a<<endl; (a++) a 两值不等
const People operator ++(int) /* 类内实现 */
{
People temp(*this);
this->m_age += 1;
return temp;
}
(3)单目运算符: << , >> , & , | ^
People operator <<(int num) /* 类内实现 */
{
People temp(*this);
temp.m_age = temp.m_age << num;
return temp;
}
单目运算符 高阶:
{ /* 类内实现: */
/* 由于遇到了 extern ostream cout 整个程序只有一个流,所以需要权限和引用 */
friend ostream &operator <<(ostream &out,const People &value)
{
out << value.m_name << "\t"
<< value.m_age << "\t"
<< value.m_sex;
return out;
}
friend istream &operator >>(istream &in,People &value)
{
cout << "请输入姓名 性别 年龄:";
in >> value.m_name >> value.m_sex >> value.m_age;
return in;
}
};
int main()
{
People peo5,peo6;
cin >> peo5; //输入
cout << peo6 << endl;; //输出
return 0;
}
(4)其他操作符:“ ()”,仿函数,通常做算法策略
仿函数,谓词
void operator ()(/*参数列表*/)
{
cout << "仿函数策略" << endl;
}
bool operator ()(int value) /* 一元谓词:一个参数 */
{
if(value == 1) return true;
return false;
}
bool operator ()(int value1,int value2) /* 二元谓词:两个参数 */
{
if(value1 == value2) return true;
return false;
}
(5)其他操作符:“ [ ] ”
{ /* 类内实现: */
int addr_len = -1;
int addr_max = 10;
int &operator [](int index)
{
return this->addr[index];
}
const int operator [](string com)
{
if(com == "max") return addr_max;
if(com == "len") return addr_len;
return 0;
}
};
int main()
{
people[3] = 5; /* 相当于 people.addr[3] = 5 ,解读当前想要操作的是int addr[3]*/
for(int i = 0; i < people["max"];i++)
{
cout << people[i] << ",";
}
cout << endl;
return 0;
}
(6)其他操作符 ” -> ”指针操作符:
{
/* 类内实现: */
class Grade /* 类内部类 */
{
public:
int language;
int math;
};
Grade grade = { /* 结构体定义时直接赋值 */
.language = 10,
.math = 20
};
Grade *operator ->()
{
return &(this->grade);
}
};
int main()
{
People peo7;
peo7->language = 100; /* peo7.grade.language = 100; */
}
10、继承:(构造函数可相互调用)
People::People(string name,string sex,int age)
:m_name(name),m_sex(sex),m_age(age)
{ }
/***************************************************************************************/
Student::Student(string id, string name, string sex, int age)
//:m_ID(id),People(name,sex,age),m_grade{0,0,0}
:Student(id,0,0,0,name,sex,age) /*构造函数相互调用*/
{ }
Student::Student(string id, float language, float math, float english, string name, string sex, int age)
:m_ID(id),People(name,sex,age),m_grade{language,math,english}
{ }
Student::Student(string id, Grade grade, string name, string sex, int age)
:Student(id,grade.language,grade.math,grade.english,name,sex,age) /*构造函数相互调用*/
{ }
11、多继承成会产生的问题:
(1)父类成员同名 实习生 继承 学生和工人 使用类域调用解决
(2)菱形继承
people
student worker <- : public virtual people
trainee
会重复继承相同的people 造成空间浪费,使用虚继承解决(生成虚表索引,虚指针 32位机占4字节)
class People
{
public:
string m_name;
int m_age;
};
class Student : virtual public People
{
public:
// string m_name;
// int m_age;
float m_score;
void Study(){cout<<"学习"<<endl;}
void Exam(){cout<<"考试"<<endl;}
};
class Worker : public virtual People
{
public:
// string m_name;
// int m_age;
int m_salary;
void work(int hour){cout<<hour<<"work"<<endl;}
};
class Trainee: public Student , public Worker
{
public:
void sleep(){cout<<"zzzZZzzzZZZZZzZ..."<<endl;}
};
int main(int argc, char *argv[])
{
cout<<sizeof(string)<<endl; //24字节
cout<<sizeof(People)<<endl; //28字节
cout<<sizeof(Student)<<endl; //People28 + float4 + 虚表索引4字节
cout<<sizeof(Worker)<<endl; //People28 + int4 + 虚表索引4字节
cout<<sizeof(Trainee)<<endl; //Student32+4 + Worker32+4 -Peole28字节
Trainee tra1;
tra1.m_name = "aaa";
cout<<tra1.Trainee::m_name<<endl;;
cout<<tra1.Worker::m_name<<endl;;
return 0;
}
12、多态:
实现方法:
- 必须在继承体系下
- 子类必须要对父类中方法进行重写
- 通过父类的引用调用重写的方法
存在的问题:
一般在父类指针指向子类对象 (实现多态) delete时 会造成内存泄漏,可以使用虚析构或纯虚析构解决实现
class A
{
public:
A(){cout<<__func__<<endl;}
virtual ~A(){cout<<__func__<<endl;}
};
class B : public A
{
public:
B(){cout<<__func__<<endl;}
~B(){cout<<__func__<<endl;}
};
/*无virtual 加virtual后
A A
B -> B
~A ~B
~A
*/
int main(int argc, char *argv[])
{
A *a1 = new B;
delete a1;
return 0;
}
13、文件操作
文件流类 头文件:#include <fstream>
(1)打开文件:
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 追加
fstream fd; /* 1.实例化对象 */
/* 2.打开文件,当前路径下清空写 */
fd.open("test.txt",ios_base::trunc | ios_base::in | ios_base::out);
/* Qt中先要配置左侧“项目”中的路径,红色路径表示不能使用,shadow build不要勾选 */
if(fd.is_open() == false) /* 3.判断是否打开成功 */
{
cout <<"文件打开失败"<< endl;
}
(2)读写操作
格式化读取: operator >>
格式化写入: operator <<
class Student
{
public:
Student(string name = "",int age = 0):m_name(name),m_age(age){}
string m_name;
int m_age;
};
Student stu1("李华",18);
Student stu2("小美",19);
Student stu3;
fd <<stu1.m_name<< " " <<stu1.m_age<< endl; /* 格式化写入 */
fd <<stu2.m_name<< " " <<stu2.m_age<< endl;
while(fd.eof() == false)
{
Student stu3;
fd >> stu3.m_name >> stu3.m_age; /* 格式化读出,写入时的空格做变量分割 */
if(stu3.m_age != 0)
cout << "姓名:" << stu3.m_name << "\t年龄:" << stu3.m_age << endl;
}
块写入: write(const char_type* __s, streamsize __n)
块读取: read(char_type* __s, streamsize __n)
char buf[128] = "你好";
fd.write(buf,128); /*写入一块*/
char buf[128] = "";
fd.read(buf,128); /*读出一块 并写入buf*/
cout << "buf = " << buf << endl;
字节写入: put(char_type __c)
字节读取: int_type get()
char buf[128] = "abcdefg";
for(int i = 0; i < strlen(buf);i++)
{
fd.put(buf[i]); /* 用循环依次写入 */
}
char ch = 0;
while(fd.eof() == false)
{
ch = fd.get(); /* 用循环依次读出,并用ch接收打印 */
cout << ch;
}
读取一行: getline(char_type* __s, streamsize __n)
char buf[128] = "";
fd.getline(buf,128);
fd.close(); /* 关闭文件 */
(3)关闭文件: fd.close();
容器:
14、vector
头文件:#include <vector>
构造: vector() 无参构造
vector(const vector &stl); 拷贝构造
vector(Iterator begin,Iterator end); 区间赋值构造
vector(int count,Type value); N个值赋值
经常用的构造: 无参 和 区间赋值构造
析构: ~vector()
赋值: =号赋值运算符重载
assign()函数 ------- 支持区间
插入和删除:
push_back() 尾部插入
pop_back() 尾部删除
insert(iterator pos,Type value) 任意插入-----支持区间
erase(iterator begin,iterator end) 任意删除 删除指定迭代器位置 或 迭代器区间
clear() 清空容器
访问:
int index;
v.at(index)
v[index](运算符重载)
v.front()
v.back()
迭代器: vector连续空间->随机访问迭代器->可使用 ++,--,[]
v.begin(),v.end() -> [ )左闭右开
注意:v.end()是最后一个元素后的位置,所以没有值
v.front() v.begin()
v.back() (--v.end())
使用for_each:
for_each(_InIt _First, _InIt _Last, _Fn _Func)
注意事项:一般算法库,策略函数参数,传参类型都是Type变量类型,for_each有三个参数,前两个参数用来确定一个区间,第三个参数则是操作方式,lambda,函数对象(仿函数)或者普通函数都可以充当其第三个参数。
int main(){
vector<int> v1,v2; /* 实例化vector对象 */
v1.push_back(1); /* push_back 插入数据 */
v1.push_back(4);
v1.push_back(9);
/* insert 插入数据 */
v2.insert(v2.begin(),v1.begin(),v1.end());
v2.insert(v2.begin(),10,1);
v2.insert(v2.begin(),99);
for(int i = 0;i < v1.size();i++)
{
cout<< v1[i] <<","; /* ①v[index]访问*/
}
cout<< endl;
vector<int>::iterator it = v1.begin();
for(;it != v1.end();it++) /* ②迭代器访问 */
{
cout<< *it <<","; /* (*it) => <类型> 尖括号内类型 */
}
cout<<endl;
}
三种for_each实现:
int main() /* ③for_each 实现 */
{
class Print
{
public:
void operator()(int value) /* 仿函数 */
{
cout<<value<<",";
}
};
/* Print pri1; for_each(v1.begin(),v1.end(),pri1); Print()为匿名对象 */
for_each(v1.begin(),v1.end(),Print());
void Print_int(int value); /* 普通函数:内部声明*/
for_each(v1.begin(),v1.end(),Print_int);
for_each(v1.begin(),v1.end(),[](){ /* lambda*/
/* 实现 */
});
}
void Print_int(int value)
{
cout << value << ",";
}
谓词:
1、返回bool类型的仿函数称为谓词
2、如果operator()接受一个参数,那么叫做一元谓词
3、如果operator()接受两个参数,那么叫做二元谓词
vector容量和大小:
empty() 判断容器是否为空
capacity() 容器的容量
size() 当前容器的元素个数
resize() 重新指定容器的长度
reserve() 预留空间 < ----- > 非常有必要
发现结果: 数据容量成指数增长,
缺点:①照成空间浪费 ②每次增长浪费时间
解决方案:开局就指定空间大小 <------> 提前分配空间
vecto r优点: 访问快,尾删和尾增快
vector 缺点: 非尾删和非尾增效率低,增加元素需要申请新空间并拷贝,浪费效率不适合(排序等需要改变的算法)
vector<int> v;
v.reserve(10001);
存放自定义类型: Person成员: 姓名 + 年龄
(要注意数据结构中对自定义类型的支持:运算符重载实现)
class Person
{
public:
Person(string name = "",int age = 0):m_name(name),m_age(age){}
string m_name;
int m_age;
private: /* friend打破封装,可无视private */
friend ostream &operator <<(ostream &out,Person person)
{
out << person.m_name << "\t"
<< person.m_age;
return out;
}
};
vector<Person> v; /* 实例化STL对象 */
v.push_back(Person("李华",17)); /* 插入数据 */
v.push_back(Person("李明",18));
//auto 自动类型推导 iterator begin()
for(auto it = v.begin() ; it != v.end() ;it++)
{
cout << *it << endl; /* 输出数据 */
}
15、deque 双端数组
头文件:#include <deque>
构造: deque() 无参构造
deque(const deque &)拷贝构造
deque(iterator begin,iterator end)区间构造
deque(int count,Type value) N个值赋值构造
赋值: =号赋值运算符重载
assign()函数 ------- 支持区间
插入和删除:
push_back() 尾部插入
push_front() 头部插入
pop_back() 尾部删除
pop_front() 头部删除
insert(iterator pos,Type value) 任意插入-----支持区间
erase(iterator begin,iterator end) 任意删除 删除指定迭代器位置 或 迭代器区间
clear() 清空容器
访问:
int index;
d.at(index)
d[index](运算符重载)
d.front()
d.back()
迭代器: 支持迭代子遍历
vector<int> v1;
deque<int> d1(v1.begin(),v2.end()); /* 可以支持不同容器 */
通用容器打印函数(使用模板):
template <class Type>
void Print(Type &v)
{
for(auto it = v.begin(); it != v.end();it++)/*auto 是自动根据 = 返回值类型做生成 */
{
cout << *it << ",";
}
cout << endl;
}
deque<int> v;
for(int i = 0; i < 10;i++)
{
v.push_back(i); /*尾部插入 */
v.push_front(i); /*头部插入 */
}
v.insert(v.begin(),3,600);
/* 600,600,600,9,8,7,6,5,4,3,2,1,0,0,1,2,3,4,5,6,7,8,9 */
cout << "删除数据:" << endl;
v.pop_back(); /*尾部删除*/
v.pop_front(); /*头部删除*/
/* 600,600,9,8,7,6,5,4,3,2,1,0,0,1,2,3,4,5,6,7,8 */
v.erase(++v.begin(),--v.end()); /* 指定删除 */
/* 600,8 */
cout << v.size() << endl; /* 元素个数 */
cout << v.empty()<< endl; /* 是否为空 */
for(size_t i = 0; i < v.size() ;i++)
{
//cout << v[i] << " "; /* [] 运算符重载 */
cout << v.at(i) << " "; /* at 下标访问 */
}
使用算法库sort排序:
void sort(首迭代子,尾迭代子) -> 排序规则:默认升序,使用 >或< 运算符重载
void sort(首迭代子,尾迭代子,二元谓词) -> 排序规则:默认升序
void test()
{
deque<int> v;
for(int i = 0; i < 10;i++)
{
v.push_front(i);
}
sort(v.begin(),v.end()); /* ①使用默认sort排序 */
bool cmp_int(int value1,int value2); /* 回调函数内部声明 */
sort(v.begin(),v.end(),cmp_int); /* ②回调函数机制自定义规则 */
class CMP_INT
{
public:
bool operator()(int value1,int value2) /*二元谓词*/
{
return value1 > value2;
}
};
//CMP_INT temp;sort(v.begin(),v.end(),temp);/* ③仿函数机制自定义规则 */
sort(v.begin(),v.end(),CMP_INT()); //CMP_INT()匿名对象
}
bool cmp_int(int value1,int value2) /* 回调函数外部实现 */
{
return value1 < value2;
}
存放自定义类型:
class People
{
public:
People(string name = "",int age = 0) : m_name(name),m_age(age){}
bool operator <(/*this,*/const People &value) const /* < 比较运算符重载 */
{
return this->m_age < value.m_age;
}
int age() const;
protected:
string m_name;
int m_age;
};
int People::age() const
{
return m_age;
}
void main()
{
deque<People> v;
string names[] = {"李华","李化","李花"};
int ages[] = {18,20,17};
for(int i = 0; i < 3;i++)
{
People people(names[i],ages[i]);
v.push_back(people);
}
sort(v.begin(),v.end()); /* ①默认升序,使用 < 比较运算符重载 */
class CMP_People
{
public:
bool operator()(People value1,People value2)
{
return value1.age() < value2.age();
}
};
sort(v.begin(),v.end(),CMP_People()); /* ②使用自定义策略:二元谓词 */
}
16、list 链表容器:
构造:
assign(beg, end); 将[beg, end)区间中的数据拷贝赋值给本身
assign(n, elem); 将n个elem拷贝赋值给本身
list& operator=(const list &lst); 重载等号操作符
swap(lst); 将lst与本身的元素互换
容器大小:
size(); 返回容器中元素的个数
empty(); 判断容器是否为空
resize(num); 重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
resize(num, elem); 重新指定容器的长度为num,若容器变长,则以elem值填充新位置。
插入和删除:
push_back(elem); 尾部加入一个元素
pop_back(); 删除最后一个元素
push_front(elem); 在开头插入一个元素
pop_front(); 移除第一个元素
insert(pos,elem); 在pos位置插elem元素的拷贝,返回新数据的位置。
insert(pos,n,elem); 在pos位置插入n个elem数据,无返回值。
insert(pos,beg,end); 在pos位置插入[beg,end)区间的数据,无返回值。
clear(); 清除
erase(beg,end); 删除[beg,end)区间的数据,返回下一个数据的位置。
erase(pos); 删除pos位置的数据,返回下一个数据的位置。
remove(elem); 删除容器中所有与elem值匹配的元素。
反转和排序:
reverse(); 反转链表
sort(); 链表排序
class People
{
public:
People(string name = "",int age = 0,string sex = "女",int height = 0);
string name() const;
int age() const;
string sex() const;
int height() const;
bool operator <(/*this*/ const People &people) const
{
return this->m_age < people.m_age;
}
bool operator ==(/*this*/ const People &people) const
{
return this->m_name == people.m_name;
}
protected:
string m_name;
int m_age;
string m_sex;
int m_height;
private:
friend ostream &operator <<(ostream &out,const People &people)
{
out << people.m_name << "\t" << people.m_age << "\t" << people.m_sex << "\t" << people.m_height;
return out;
}
};
void test0()
{
list<People> v;
string names[] = {"李华","李花","李化","立华","黎华"};
int ages[] = {18,29,31,18,30};
string sexs[] = {"男","女","男","男","女"};
int heights[] = {160,175,183,159,165};
for(int i = 0; i < 5;i++)
{
v.push_back(People(names[i],ages[i],sexs[i],heights[i])); /*匿名对象 */
}
list<People>::iterator it; /*定义迭代器*/
for(it = v.begin(); it != v.end() ; it++)
{
cout << *it << endl; /* 打印数据<<重载 */
}
}
list自带 sort 排序有两个:void sort(); void sort(二元谓词);
void test1()
{
v.sort(); /* ①list自带排序规则: 使用<运算符重载 */
v.reverse(); /* 链表反转 */
class CMP_People
{
public:
bool operator()(const People &people1,const People &people2)
if(people1.age() == people2.age()) //年龄相同
{
return people1.height() < people2.height(); //比较身高
}
return people1.age() < people2.age();
}
};
v.sort(CMP_People()); /* ②二元谓词规定排序规则*/
}
查找:find( begin() , end() ,参考变量指定的排序规则)
查找第一个匹配的元素,并返回当前迭代子
find_end 找到最后一个匹配的元素
find_first_of 找到第一个匹配的元素
find_if 找到第一个匹配一元谓词的元素
find_if_not 找到第一个匹配的一元谓词的元素
返回值:
找到:返回非 end尾迭代子
未找到: end尾迭代子
注意:需要 == 判断运算符重载
/* class People
{
.....
bool operator ==((this默认值,可省略) const People &people) const
{
return this->m_name == people.m_name;
}
.....
}; */
void test3()
{
it = find(v.begin(),v.end(),People("李华")); /* 匹配姓名 ==重载 */
if(it != v.end())
{
cout << "找到了" << endl;
cout << *it << endl;
}
else
cout << "未找到" << endl;
class CMP_People1
{
public:
bool operator()(const People &people) /* 匹配性别 仿函数指定规则 */
{
return people.sex() == "男";
}
};
for(it = v.begin(); it != v.end() ; it++)
{
it = find_if(it,v.end(),CMP_People1()); /* find_if找到第一个匹配一元谓词的元素*/
if(it != v.end())
{
cout <<"it != v.end()"<< *it << endl;
}
else
break;
}
}
17、string容器:
构造:string() 无参
string(str) 拷贝构造
string( str, pos, n ) 指定位置个数拷贝{区间}
string(Char*) C字符指针
string(n, Char) 多个字符
string(beg, end) 迭代子区间
string->vector <char>:
vector<char> v(str3.begin(),str3.end());
void test0()
{
string str1; /*无参构造*/
string str2(str1); /*拷贝构造*/
string str3("hello world"); /*C字符指针*/
string str4(str3,1,2); /*指定位置 个数拷贝{区间}*/
string str5(10,'A'); /*多个字符*/
vector<char> v(str3.begin(),str3.end());
string str6(v.begin(),v.end());
string str7(str3.begin(),str3.end());
}
赋值:
=号赋值: 类似C字符串库的 strcpy
string&operator=(str) C++风格
string&operator=(Char*) C风格
string&operator=(c) 单字符
assign赋值: 类似C字符串库的 strncpy
string&assign(first, last) 区间
string&assign(str) C++风格
string&assign(str, pos, n) 类似 strncpy C++格式
string&assign(Char* ,n) 类似 strncpy C格式
string&assign(const _CharT* __s) 类似 strcpy C格式
string&assign(n, Char) 多字符
void test1()
{
string str("hello");
string str1;
str1 = str; /*C++风格*/
str1 = "hello world"; /*C风格*/
str1 = 'N'; /*单字符*/
str1.assign(str.begin(),str.end()); /*区间*/
str1.assign(str);
str1.assign(str,0,2); /*C++风格strncpy*/
str1.assign("hello wrold",5); /*C风格strncpy*/
str1.assign("hello wrold"); /*C风格strcpy*/
str1.assign(100,'6'); /*多字符*/
}
转换:
类似于C库中的转换形式 atoi , atol , itoa ,ltoa, dtoa
数值转string:
string to_string(double);
string to_string(flaot);
string to_string(int);
...
string转数值:
double stod();
flaot stof();
int stoi();
...
string转char*指针:
内部.data()
void test2()
{
int value_int = stoi(string("-10")); /*string 转数值*/
double value_double = stod(string("15.6"));
string str; /*数值 转string*/
str = to_string(165);
str = to_string(165.56);
char buf[128];
strcpy(buf,str.data()); /*string 转char* */
}
拼接:
+= 运算符重载 类似 strcat
append() 类似 strncat 和 strcat
void test3()
{
string str;
str += 'A';
str += string("李华");
str += "骊骅";
/*append*/
str.append(str.begin(),str.end()); /*区间*/
str.append(10,'A');
str.append("狸花");
}
查找和替换和子串
查找:find 类似C库的 strchr(char) 和 strstr(char)
返回值:下标
未找到:-1
找到:!= -1
替换: replace 插入式覆盖 又是类似于 assign 的参数,增加下标
子串: string substr(); ,操作类似strncpy + strstr
void test4()
{
string str("hello world");
int index = 0;
index = str.find('P'); /*查找*/
int index_head = str.find("wo");
int index_end = str.find("ld");
str.replace(0,5,"helloooooooo! world"); /*替换*/
index_head += strlen("world");
string v = str.substr(index_head,index_end-index_head);/*子串*/
}
100、QT成员名在相应源文件生成函数 方法:
101、
多文件操作:
.h
#include <iostream>
using namespace std;
class
.cpp
#include " .h"
函数实现。。。
接口函数设为常函数
main.cpp
#include<iostream>
#include <list>
#include " .h"