编译和调试的区别
编译(compile):依赖于编译器,将源码转化成目标文件 如.obj
调试:让程序在系统中运行之前查错和改错,找出并修正潜在的错误
linux c++的四个处理过程
4种转换较为规范的运算符
reinterpret_cast
枚举类型 enum
定义:
enum 类型名{枚举值表}
枚举值表也叫枚举元素列表,列出定义的枚举类型的所有可用值,各个值之间用“,”分开。
enum Suit{Diamonds,Hearts,Clubs};
c++的运算符优先要求使用括号
数组类比于指针
数组名是第一个元素的地址
int* a的类型是int* int a的类型是int
前者a是指向整型数据的指针,它的本质是一个地址,后者就是一个数据了
Dog* dog = new Dog;
分配一片内存 并把内存的地址赋给 dog
不能存储负数值的无符号变体,其优点是可以增大变量能够存储的最大值
(short范围-32768-32767,unsign short范围0-65535)
使用new[]运算符创建数组时,将采用动态联遍,即将在运行时为数组分配空间,其长度也将在运行时设置
使用完成后,应用delete[]释放占用的内存
int size;
cin >>size;
int *arr = new int [size];
……
delete[] arr;
指定元素区间 可以通过传递两个指针完成:一个指针标识数组的开头,另一个指针标识数组的尾部
int sum_arr(const int *begin,const int *end); //函数声明
int main(){
int arr[10];
int sum = sum_arr(arr,arr+3); //定义区间
}
为防止函数无意中修改数组的内容,可在声明形参时使用关键字const
void show_array(const double ar[],int n)
struct typename {
char name[10];
int ages;
}
inflat *ps = new inlat;
使用完成后要用delete释放内存 delete ps;
ps -> price 也是指向结构的price成员
ps 是指向结构的指针,则*ps就是指向的值–结构本身
(*ps)是一种结构 (*ps).name 是该结构的name成员
原型可以帮助编译器完成许多工作,降低程序出错的机率,具体有一下几点:
(文本替换)
define 是宏定义,程序在预处理阶段将用define定义的内容进行替换。程序在运行时,常量表中并没有用define定义的常量,系统不会为它分配内存
#define定义一个标识符来表示一个常量。
特点:定义的标识符不占内存,只是一个临时的符号,预编译后这个符号就不存在了
定义标识符的一般形式:
#define 标识符 常量 //注意,最后没有分量
#ifdef WINDOWS
……
#endif
#ifdef LINUX
……
#endif
可以在编译的时候通过#define设置编译环境。
使用函数指针的三个步骤:
声明函数指针
double pam(int) //函数原型
double (*f)(int) //函数指针
pam 替换为了(*f).由于pam是函数名 即(*f)也是函数名, f 是函数指针
函数指针调用函数
//函数指针调用函数
f = pam;
double y = pam(2);
double x = (*f)(5);
#include
using namespace std;
double jack(int);
double rose(int);
void accrate(int lines, double(*pf)(int)); //声明指针函数
int main() {
int code;
cin >> code;
cout<<"jack和rose输入"<<code<<"行代码算花费的时间"<<endl;
accrate(code, jack);
accrate(code, rose);
}
double jack(int n) {
double sum = n * 0.5;
return sum;
}
double rose(int n) {
double sum = n * 0.7;
return sum;
}
void accrate(int lines, double(*pf)(int)) {
//double um = (*pf)(lines);
cout << lines << " lines may have " << (*pf)(lines) << "mins" << endl;
}
#include
void swap1(int a, int b) {
int temp;
temp = a;
a = b;
b = temp;
}
void swap2(int* p, int* q) {
int temp;
temp = *p;
*p = *q;
*q = temp;
}
void swap3(int& a, int& b) {
int temp;
temp = a;
a = b;
b = temp;
}
int main() {
int i = 10;
int j = 20;
//按值传递
swap1(i, j); //形参不会对实参进行修改
std::cout << "i的值是" << i << std::endl;
std::cout << "j的值是" << j << std::endl;
//按地址传递
swap2(&i, &j); //形参会对实参进行修改
std::cout << "i的值是" << i << std::endl;
std::cout << "j的值是" << j << std::endl;
//按引用传递
swap3(i, j); //形参不会对实参进行修改
std::cout << "i的值是" << i << std::endl;
std::cout << "j的值是" << j << std::endl;
return 0;
}
每声明一个模板函数 都必须有一下两行声明;
template<typename T>
void swap(T a,T b){
T temp;
temp = a;
a = b;
b = temp;
}
建立一个模板,关键字template和typename是必需的
swap(a,b);
swap(a,b);
需要多个对不同类型使用同一种算法的函数时,可以使用模板。可以像重载常规函数定义那样重载模板定义。
#include
const int size = 8;
template<typename T>
void swap(T& a, T& b) {
T temp;
temp = a;
a = b;
b = temp;
}
template<typename T>
void swap(T* a, T* b, int n) {
T temp;
for (T i = 0; i < n; i++) {
temp = a[i];
a[i] = b[i];
b[i] = temp;
}
}
void show(int a[]) {
for (int i=0;i<size;i++)
{
std::cout << a[i];
}
std::cout << std::endl;
}
int main() {
int n = 10;
int m = 20;
swap(n, m);
std::cout << "n的值是" << n << std::endl;
std::cout << "m的值是" << m << std::endl;
int a[size] = { 1,2,3,4,5,6,7,8 };
int b[size] = { 9,8,7,6,5,4,3,2 };
swap( a, b,size);
show(a);
}
建立一个通用类,类中的成员 数据类型可以不做具体指定
template<typename T>
类
#include
using namespace std;
template<typename T1, typename T2>
class Person {
public:
Person(T1 name, T2 age);
T1 m_name;
T2 m_age;
};
template<typename T1, typename T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
this->m_age = age;
this->m_name = name;
}
void test()
{
Person<string, int> p1 = Person<string, int>("zhangsan", 18); //无自动推导 要在积极手动加入
Person<string, int> p2("lisi", 20);
cout << p1.m_age << p1.m_name << endl;
cout << p2.m_age << p2.m_name << endl;
}
int main() {
test();
}
类模板与函数模板的区别
void test()
{
Person<string, int> p1 = Person<string,int>("zhangsan", 18);
Person<string, int> p2("lisi", 10);
}
template<typename T1,typename T2=int> //默认类型
class Person {
public:
Person(T1 name, T2 age);
T1 m_name;
T2 m_age;
void showmess();
};
//调用
Person<string> p1("zhangsan",19);
模板函数中成员函数的创建时机
类模板中的成员函数 并不是一开始就创建的,而是在模板调用时再生成
类模板对象做函数参数
//指定传入类型
void showperson1(Person<string,int>&p) {
cout << "姓名1:" << p.m_name << "年龄1 :" << p.m_age << endl;
}
void test1() {
Person<string, int > p1("zhangsan", 18);
showperson1(p1);
}
模板类分文件编写 可以将.h和.cpp中的内容写到一起,将后缀名改成.hpp
类模板与友元
实现步骤
template<typename T1, typename T2>
class Person;
template <typename T1, typename T2>
void show(Person<T1, T2> p)
{
std::cout << p.m_age << std::endl;
}
template<typename T1,typename T2>
class Person
{
// 加一个空模板的参数列表
friend void show<>(Person<T1,T2> p);
public:
Person(T1 name,T2 age);
private:
T1 m_name;
T2 m_age;
};
void test() {
Person<std::string, int> p1("zhangsan", 16);
show(p1);
}
指定类设计的第一步是提供类声明。类声明类似结构声明,包括数据成员和函数成员。
class person{
private:
//数据项目通常放在私有部分
char name[10];
int ages;
public:
//组成类接口的成员函数放在公有部分
void eating();
void sleep();
}
公有部分的内容构成了设计的抽象部分————公有接口
成员函数的函数头使用(::)来指出函数所属的类
类方法可以访问类的private组件
:: 作用域解析符
void person ::sleep( );
#include
#include
//std::cout.setf(std::ios_base::fixed, std::ios_base::floatfield);
class Stock //定义Stock类
{
public:
void acquire(const std::string& co, long n, double pr);
void buy(long num, double price);
void sell(long num, double price);
void update(double price);
void show();
private:
std::string company;
long shares;
double share_val;
double total_val;
void set_tot() { total_val = share_val * shares; };
};
void Stock::acquire(const std::string& co, long n, double pr) //
{
company = co;
if (n < 0) {
std::cout << "no shares";
shares = 0;
}
else
shares = n;
share_val = pr;
set_tot();
}
void Stock::show() {
using namespace std;
cout << "company s shares is " << shares << endl;
cout << "company s shares is " << share_val << endl;
cout << "company s shares is " << total_val << endl;
}
int main() {
Stock tencent, ali; //创建两个Stock对象
tencent.acquire("baidu", 20, 12.50);
tencent.show();
return 0;
}
在内存四区中,静态变量存储在全区局,程序结束运行后才会释放。
静态变量不能被其他文件所用,其他文件可以定义相同的变量名称,不会冲突
全局变量默认是有外部链接性的,作用域是整个工程,其他文件想要访问,可以使用extern
在C++中,静态成员是属于整个类的而不是某个对象。静态成员可以通过双冒号来使用<类名>::<静态成员名>
1. 类的对象可以使用静态函数和非静态函数。
2. 静态函数中不能引用非静态成员变量,非静态函数可以引用静态变量
3. **类的静态成员变量在使用前必须先初始化** `int student::n = 0;`
#include
class student {
public:
static void show1(int n);
void show2();
private:
static int n; //声明静态局部变量
double s;
};
int student::n = 0; //定义并初始化静态成员变量
void student::show1(int m) {
student::n = m;
std::cout << "static的作用" << std::endl;
std::cout << "static变量n的值: " << std::endl;
}
void student::show2() {
std::cout << "showtime" << std::endl;
}
int main() {
student li;
student::show1(5);
/*student::n = 10;*/
li.show1(3); //对象调用静态成员函数
li.show2();
}
专门由于构造新对象、将值赋给它们的数据成员
名称与类名相同
Stock类的构造函数Stock()
声明类构造函数
Stock(const string &co,long n = 0,double pr = 0.0) //声明类构造函数
定义构造函数
//定义构造函数
Stock ::Stock(const string & co, long n,double pr){
}
与普通函数不同的是,程序声明对象时,将自动调用构造函数
使用构造函数
Stock stock1 = Stock("zkhx",3,2.1);
Stock stock1("zkhx",3,2.1);
stock1.show();
~DataTimerTest()
函数前面加~表示析构函数
就像对象被创建时程序将调用构造函数一样,当对象结束生命周期,如对象所在的函数已调用完毕时,系统自动执行析构函数,释放内存
(每个类只能有一个析构函数)
//析构函数
data_queue::~data_queue() {
if (m_minBufferData) {
delete m_minBufferData;
m_minBufferData = NULL;
}
if (m_maxBufferData) {
delete m_maxBufferData;
m_maxBufferData = NULL;
}
}
如函数使用了指针变量,析构函数中加入delete
delete释放指针原本所指的内存,指针被delete后,如果不置为null,指针就会成为野指针,会在内存乱指一通。,再次调用可能会导致系统崩溃。
对一个非空指针delete后,若没有赋null,再次delete是不可行的
第一次delete指针后,指针的地址并不是空的,同一块内存释放两次会导致崩溃。
总结: delete一个空指针是合法的,为了防止多次delete指针导致程序崩溃,需要养成良好的置空(null)习惯
解决名称冲突
this指针指向 被调用的成员函数 所属的对象
class Person{
public:
Person(int age){
this->age = age;
}
Person& addage(Person &p){
this->age +=p.age;
return *this; //this 指向p2的指针,而*this指向的就是p2这个对象的本体
}
int age;
}
void test(){
Person p1(18);
cout <<p1.age<<std::endl;
}
返回对象本身用*this
链式编程思想
Person& Person::addage(Person& p)
{
this->age += p.age;
return *this;
}
p2.addage(p1).addage(p2) //链式编程思想
如果不用引用的方式返回,相当于返回与p2不同的另一个Person(只是age都是20),那么后续的加年龄操作与p2就没有关系了;
返回是值的话,相当于创建了一个新的对象,同时调用了拷贝构造函数
通常要创建同一个类的多个对象。声明对象数组的方法与声明标准类型数组相同
stock mystuff[4]; //创建一个对象数组
每个元素(mystuff[0],mystuff[1])都是stock的对象
mystuff[0].update();
可以用构造函数初始化数组元素。在这种情况下,必须为每个元素调用构造函数:
const int STK =4;
Stock stock[STK] = {
Stock("NanoSmart",12.3,30);
Stock("zkhx",12,4);
Stock();
};
对已有的运算符重新进行定义,赋予另一种功能,以适应不同的数据类型
实现自定义的加法运算
#include
using namespace std;
class Person
{
public:
//1.成员函数重载+号
Person operator+(Person& p) {
Person temp;
temp.m_a = this->m_a + p.m_a; //this只能用于非静态成员函数
temp.m_b = this->m_b + p.m_b;
return temp;
}
int m_a;
int m_b;
};
//2. 全局函数重载
Person operator+(Person& p1, Person& p2) {
Person temp;
temp.m_a = p1.m_a + p2.m_a;
temp.m_b = p1.m_b + p2.m_b;
return temp;
}
Person operator+(Person& p1, int n) { //运算符重载外加函数重载
Person temp;
temp.m_a = p1.m_a + n;
temp.m_b = p1.m_b + n;
return temp;
}
void test01() {
Person p1;
p1.m_a = 10;
p1.m_b = 10;
Person p2;
p2.m_a = 10;
p2.m_b = 10;
//成员函数重载本质
Person p3 = p1.operator+(p2);
//全局函数重载本质
Person p3 = operator+(p1, p2);
Person p3 = p1 + p2;
cout << "p3.m_a的值 " << p3.m_a << endl;
cout << "p3.m_b的值 " << p3.m_b << endl;
//运算符重载 也可以发生函数重载
Person p4 = p1 + 15; // Person + int
cout << "p4.m_a的值 " << p4.m_a << endl;
cout << "p4.m_b的值 " << p4.m_b << endl;
}
int main() {
test01();
return 0;
}
全局函数可以改变运算顺序 成员函数不行
总结1:对于内置的数据类型的表达式的运算符是不可修改的
总结2:不要滥用运算符重载
下面的运算符只能通过成员函数进行重载
= 赋值运算符
( ) 函数调用运算符
[ ] 下标运算符
-> 通过指针访问类成员的运算符
实现自定义的输出
void operator<<(ostream& cout, Person& p);
cout << p;
这种重载方式只能加载左右两个重载对象
ostream& operator(ostream& cout,Person& p){
cout << x << y;
return cout;
}
<<运算符要求左边有一个ostrem对象。所以表达式 满足这种要求。然而,表达式 成员函数不能改变运算顺序 全局函数可以 但非成员函数不能直接访问类的私有数据。这时候就要引入一个特殊的函数 友元函数 创建友元函数的第一步是将其原型放在类声明中,并在前面加上一个friend 虽然operator放在类声明中,但它不是成员函数 不能使用成员符号调用 友元函数不是成员函数 不能使用类::限定符。定义中不用加friend 在想要访问的类中加入友元friend类 下级别的成员有上一级的共性,还有自己的特性 减少重复代码 语法: class 子类: 继承方式 父类 继承方式 三种继承法是,父类的私有部分,任何一种方式都不能继承。 父类: 在父类中所有非静态成员属性都会被子类继承下去,父类私有成员属性,是被编译器给隐藏了,因此访问不到,但是确实被继承了。 继承中,父类子类构造和析构的顺序 在继承的过程中,子类继承父类,他们的顺序可以类比于栈的过程 先构造父类 在构造子类 接在析构子类 再析构父类 同名成员处理 子类在继承父类时有同名变量 如果通过子类对象访问 父类同名成员,需要加作用域 总结: 同名静态函数与普通函数访问的方式一致,只是要加上static的属性,有两种访问方式1.按类名访问2.按对象访问 多重继承为防止二义性 可以用::作用域 虚基类 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费,可以使用virtual加以避免 区别: 动态多态满足的条件 重写:函数返回值 函数名 参数列表 完全一致 动态多态的使用 父类的指针或引用指向子类的对象 //动态多态的使用 //父类的指针或引用 执行类对象 1. 方法一 父类的引用指向子类对象 2. 方法二 父类的指针指向子类对象 什么类型的指针都占用四个字节 加了个virtual 等于加了个指针vfptr(virtual function pointer) 非静态成员函数不属于类的对象上 多态中,通常父类中的虚函数的实现毫无意义,主要都是调用子类重写,可以把这类函数改成纯虚函数 当类中有了纯虚函数,这个类也称为抽象类 抽象类的特点 string是一个类,类内部封装了char*,管理这个字符串,是一个char*型的容器 find delete insert string构造函数 string赋值操作 string的赋值方式很多,operator=这种方式是比较实用的 字符串拼接 string +=str1 string查找和替换 find和rfind replace 字符串的比较 string字符存取 string字符串的插入和删除 插入和删除起始下标都是从0开始的 string子串 与数据相似,也成为单端数组(类比于栈) vector与普通的数组的区别 数组时静态空间,而vector可以动态扩展 动态扩展 并不是在原有的基础上进行拓展,而是找一个更大的空间,将原数据拷贝到新空间,释放原空间 vector迭代器 支持随机访问的迭代器 vector四种构造方法 vector的增删查 支持随机访问的容器 都可以用标准算法sort进行排序 由于链表的存储方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,属于双向迭代器 list 不支持随机存取 List有一个重要的性质,插入操作和删除操作都不会造成原有list迭代器的失效,这在vector是不成立的 STL中list和vector是两个最常使用的容器,各有缺点 list自定义类型排序 自定义类型 类类型or结构类型 要先指定类型规则 语法 所以的元素都会在插入时自动排序 set与multiset属于关联容器,底层结构是二叉树实现 区别 set的插入使用的是insert,没有push_back这个方法 empty(); size(); swap(); 插入删除 insert() erase(迭代器) erase(容器元素) clear(); 清空 查找和统计 find(); //查找key是否存在,返回该元素的迭代器不存在 返回set.end() cout(); 要么是0 要么是1 set的默认排序规则是从小到大,改变排序规则从大到小 利用仿函数,改变排序规则 map所有元素都是一对 第一个key值(索引的作用)第二个valu值(实值) 所有元素都会根据元素的键值自动排序 关联容器,二叉树实现 可以通过key值快速的找到value值 map与multimap 的区别 是能不能插入重复的key值 查找 统计 cout() 0or1 使用仿函数改变map的默认排序 由大到小 仿函数 传入的时候传入一个数据类型,在类中重载了()函数 重载函数调用操作符的类,其对象成为函数对象 对()重载的函数 也叫仿函数 算术仿函数 关系仿函数 自定义降序仿函数定价与 自定义 算法主要是由头文件 最大的一个,涉及比较、交换、查找、遍历仿函数 常用遍历算法 搬运的目标容器必须提前开辟空间,否则无法正常搬运 常用的查找算法 按条件查找 二分查找 对于有序的数列 统计自定义类型时 需要配合重载 自定义数据类型 就在自定义的类型里面就行仿函数的定义 内建型 就添加写一个类对运算符进行重载 常用排序算法 random_shuffle(it.beg(),it.end()) 常用的拷贝和替换算法 两个容器元素合并,并储存在另一个容器 将组件函数放在独立的文件中,然后将他们链接成可执行的程序。如若要修改文件,只需重新头文件,然后将它与其他文件编译版本链接,程序的管理更为便捷 组织策略 可以把程序拆分为三个部分 eg: eg: 1.高效的组织策略,编写另一程序,需要使用这些函数时,只需包含头文件,并将函数文件添加到项目列表或make列表中即可。 头文件包含的内容: 头文件编写 名称可以是函数、变量、结构、类以及类的成员,随着项目的增大,名称相互冲突的可能性也将增加 名称空间是开放的(open), using声明将特定的名称添加到它所属的声明区域中。 using声明使一个名称可用,而using编译指令使所有名称都可用。 using namespace 总结: 导入名称时,首选使用作用域解析运算符或using声明的方法。 const关键字放在函数的括号后面 stock类中的函数声明 函数定义的开头 只要类不修改调用对象,就应将其声明为const 构造函数做好使用成员初始列,而不要在构造本体函数内使用赋值操作cout<
cout<
cout<
<
因此,ostream类将operator<<()
函数实现为返回一个指向ostream对象的引用#include
友元函数
全局函数做友元
friend Person operator*(double m ,Person &p2)
operator不是成员函数 但是它有访问私有变量
class Person {
public:
friend void operator*(double n, Person& p1);
private:
int m_a;
int m_b;
};
void operator*(double n, Person& p1) {
p1.m_a = 10;
p1.m_b = 23;
Person total;
total.m_a = n * p1.m_a;
total.m_b = n * p1.m_b;
std::cout << total.m_a << std::endl;
}
类做友元
#include
成员函数做友元
friend void Goodgay::visit();
class Building {
public:
friend void Goodgay::visit(); //将成员函数写在要访问的类中
Building();
string m_livingroom;
private:
string m_bedroom;
};
类继承
保护继承将父类的公有的,保护的都转为子类保护的,私有继承将父类公有的,保护的都转为子类保护的class Pro{
public:
int a;
protected:
int b;
private:
int c;
}
公共继承: 私有继承: 保护继承:
class A :public Pro{ class B:private Pro{ class C:protected Pro{
public: private: protected:
int a; int a; int a;
protected: int b; int b;
int b; 不可访问: 不可访问:
不可访问: int c; int c;
int c; }; };
};
Son s;
std::cout << "使用子类同名变量" << s.m_a << std::endl;
std::cout <<"子类对象使用父类同名变量"<<s.Base::m_a << std::endl;
菱形继承
#include
多态
静态 编译阶段确定函数地址
动态 运行阶段确定函数地址
class Animal
{
public:
virtual void speak() {
cout << "动物说话" << endl;
}
};
class Cat :public Animal {
public:
void speak() {
cout << "小猫叫" << endl;
}
};
void dospeak(Animal& animal) //Animal & animal = cat;
{
animal.speak();
}
void test() {
Cat cat;
dospeak(cat);
void dospeak(Animal* animal) //Animal * animal = new Cat;
{
animal->speak();
}
void test() {
dospeak(new Cat);
纯虚函数
virtual 返回值类型 函数名(参数列表) = 0;
#include
String类
#include
void test01() {
string str1 = "abcdefg";
int n =str1.find('cf'); //从左往右找 查到了 返回下表索引,没找到 返回-1
cout << n << endl;
int j = str1.rfind("de"); //从右往左找
cout << j << endl;
}
void test02() {
string strl = "abcdefghi";
strl.replace(1, 3, "233"); //a索引1到3 替换成233
cout << strl << endl;
}
char &operator[] (int n);
通过[]方式取字符char & at(int n)
通过at方法取字符void test() {
string str = "hello";
//通过[]访问单个字符
for (int i = 0; i < str.size(); i++) {
cout << str[i];
}
cout << endl;
// 通过at访问单个字符
for (int i=0;i<str.size();i++)
{
cout << str.at(i);
}
cout << endl;
str[0] = 'x'; //使用[]修改字符串内容
cout << str;
}
void test() {
string str = "hello";
//插入
str.insert(3, "liu");
cout << str << endl;
//删除
str.erase(3);
cout << str << endl;
}
substr(1,3)
void test1() {
string email = "[email protected]";
int n = email.find('@');
cout << email.substr(0, n) << endl;
}
STL模板
vector容器
#include
struct review
{
string title;
int rating;
};
bool Fillreview(review& re);
void printbook(vector<review>& b1);
int main() {
vector<review>book;
review temp;
while (Fillreview( temp)) {
book.push_back(temp);
}
printbook(book);
vector<review>oldbook(book);
printbook(oldbook);
book.erase(book.begin() + 1, book.begin() + 3); //vector的删除
printbook(book);
book.insert(book.begin(), oldbook.begin() + 1, oldbook.begin() + 2);//vector的插入
printbook(book);
book.swap(oldbook);
printbook(book);
return 0;
system("pause");
}
bool Fillreview(review& re)
{
cout << "please enter a book name" << endl;
getline(cin,re.title);
if (re.title == "quit")
{
return false;
}
else
cin >> re.rating;
if (!cin)
return false;
while (cin.get()!='\n')
{
continue;
}
return true;
}
void printbook(vector<review>& b1)
{
for (vector<review>::iterator it =b1.begin();it!=b1.end();it++)
{
cout << (*it).rating <<" "<<(*it).title<< endl;
}
cout << "--------------------" << endl;
}
list双向循环链表
list.reverse();
双向链表的反转list.sort()
双向链表的排序font
首元素back
尾部元素
//指定排序规则 如果年龄相等 按照体重排序
bool compareage(Person& p1, Person& p2) {
if (p1.m_age == p2.m_age) {
return p1.m_weight < p2.m_weight;
}
else
{
return p1.m_age < p2.m_age;
}
}
#include
#include
关联容器set,mutiset
set容器排序规则
map/multimap容器
void printmap(map<int, int>& ma) {
for (map<int,int>::const_iterator it =ma.begin();it!=ma.end();it++)
{
cout << "key= " << (*it).first << " value=" << (*it).second << endl;
}
cout << endl;
}
void test() {
//map容器
map<int, int>m; //k v 创建map容器
m.insert(pair<int, int>(1, 10));
m.insert(pair<int, int>(2, 28));
m.insert(pair<int, int>(4, 20));
m.insert(pair<int, int>(6, 21));
m.insert(pair<int, int>(9, 320));
m.insert(make_pair(10, 22)); //make_pair 插入
printmap(m);
map<int, int>m2(m); //拷贝构造函数
printmap(m2);
map<int, int>m3;
m3 = m2; //赋值
printmap(m3);
cout << m[6] << endl; //可以使用[]查找;
cout<<m.size()<<endl;
//删除
m.erase(m.begin()); //按照迭代器删除
m.erase(6); //按照key值删除
printmap(m);
}
find()
查找key值是否存在,不存在返回set.end;map<int, int>::iterator it = m.find(9);
cout << (*it).first <<it->second<<endl;
重载()class Mycompare {
public:
bool operator()(int v1, int v2) const 自定义排序规则
{
return v1 > v2;
}
};
void printmap(map<int, int,Mycompare>& ma) {
for (map<int, int>::const_iterator it = ma.begin(); it != ma.end(); it++)
{
cout << "key= " << (*it).first << " value=" << (*it).second << endl;
}
cout << endl;
}
void test() {
//map容器
map<int, int,Mycompare>m; //k v 创建map容器
m.insert(pair<int, int>(1, 10));
m.insert(pair<int, int>(2, 28));
m.insert(pair<int, int>(4, 20));
m.insert(pair<int, int>(6, 21));
m.insert(pair<int, int>(9, 320));
m.insert(make_pair(10, 22));
printmap(m);
}
函数对象
#include
内建仿函数
#include
greater
-> 需要使用#include
class Mycompare {
public:
bool operator()(int v1, int v2) const 自定义排序规则
{
return v1 > v2;
}
};
STL-常用算法
for_each(iterator_beg;iterator_end;函数 仿函数);
#include
transform(v.begin(),v.end(),target.begin(),fun_函数)
class Transform {
public:
int operator()(int v) {
return v;
}
};
class Print {
public:
void operator()(int val) {
cout << val << endl;
}
};
void test() {
vector<int>v1;
for (int i=1;i<10;i++)
{
v1.push_back(i);
}
vector<int>target;
target.resize(v1.size()); //目标容器需要提前开辟空间
transform(v1.begin(), v1.end(), target.begin(), Transform());
for_each(target.begin(), target.end(), Print());
}
find
find(iterator.beg(),iter.end(),value)
find 可以在容器中找指定的元素,返回值是迭代器
自定义类型 需要写仿函数 对"=="就行重载class Person {
public:
Person(string name, int age) {
this->m_name = name;
this->m_age = age;
}
string m_name;
int m_age;
bool operator ==(Person p2 ) {
if (this->m_age==p2.m_age&&this->m_name==p2.m_name)
{
return true;
}
}
};
find_if
vector
binary_search
bool binary_search(it.beg(),it.end(),查找值)
bool ret = binary_search(v1.begin(), v1.end(), 8);
cout << ret << endl;
count
operator==
count_if
class mix14 //谓词
{
public:
bool operator()(int v1) {
return v1 > 12;
}
};
int num =count_if(v1.begin(), v1.end(), mix14());
sort(it.beg(),it.end(),谓词)
class ree {
public:
bool operator()(int v1, int v2) {
return v1 > v2;
}
};
sort(v1.begin(), v1.end(), ree());
srand((unsigned int)time(NULL)); //实时时间
vector<int>v1;
for (int i=1;i<10;i++)
{
v1.push_back(i);
}
random_shuffle(v1.begin(), v1.end());
for_each(v1.begin(), v1.end(), print);
void test() {
vector<int>v1;
vector<int>v2;
for (int i=1;i<10;i++)
{
v1.push_back(i);
v2.push_back(i + 2);
}
vector<int>target;
target.reserve()
target.resize(v1.size() + v2.size()); //目标容器定义大小
merge(v1.begin(), v1.end(), v2.begin(), v2.end(), target.begin());
for_each(target.begin(), target.end(), myprint);
}
reverse(target.begin(), target.end());
容器元素反转copy
容器内指定范围的元素拷贝到另一容器总· vector<int>tar2;
tar2.resize(6);
copy(target.begin(), target.begin() + 6, tar2.begin());
replace
replace(tar3.begin(), tar3.end(), 5, 500);
//将区间的5 替换成500replace_if
//谓词
class mix5 {
public:
bool operator()(int val) {
return val > 5;
}
};
replace_if(tar3.begin(), tar3.end(), mix5(), 3000);
内存模型和名称空间
单独编译
#ifndef STOCK_H_
#define STOCK_H_
struct polar
{
double distance;
double angle;
};
struct rect
{
double x;
double y;
};
polar rect_to_polar(rect xypos);
void show_polar(polar dapos);
#endif
#include
#include
2.这种组织方式也是面向对象(OOP)的体现
#ifndef HEAD_H_ //根据include名选择名称,并加上下划线 以防止被其他地方定义
#define HEAD_H_
…… //file content
#endif
名称空间 -ing
namespace jack{
double pail;
void fetch();
int pal;
struct well{}
}
namespace jack{
char name[10];
}
namespace jack{
void fetch(){
……
}
}
using声明和using编译
using jack::pail; // 一个using声明
int main(){
using jack::pail;
cin >> pail;
cin >> ::pail; //读入进去一个pail全局变量
}
using namespace jack; //一个using编译声明
const成员函数
void show() const //函数声明
void stock :: show() const //函数定义 不被改变
effective C++
永远在使用对象之前先将它初始化