#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <stdexcept>
#include "string"
#include <iomanip>
using namespace std;
/*
有关构造函数
1、构造函数定义及调用
1)C++中的类可以定义与类名相同的特殊成员函数,这种与类名相同的成员函数叫做构造函数;
2)构造函数在定义时可以有参数;
3)没有任何返回类型的声明。
2、构造函数的调用
自动调用:一般情况下C++编译器会 自动调用 构造函数
手动调用:在一些情况下则需要手工调用构造函数
有关析构函数
3、析构函数定义及调用
1)C++中的类可以定义一个特殊的成员函数清理对象,这个特殊的成员函数叫做析构函数
语法:~ClassName()
2)析构函数没有参数也没有任何返回类型的声明
3)析构函数在对象销毁时自动被调用
4)析构函数调用机制
C++编译器自动调用
*/
#if 0
class Test
{
public:
Test()//无参数构造函数
{
cout << "我是构造函数 被执行了" << endl;
p = (char *)malloc(100);
strcpy(p, "hello world");
a = 10;
}
void print()
{
cout << p << endl;
cout << a << endl;
}
~Test()//析构函数
{
if (p != NULL)
{
free(p);
}
cout << "我是析构函数 被调用了" << endl;
}
private:
int a;
char *p;
protected:
};
//给对象搭建一个舞台,研究对象的行为
//注意对象的生命周期,只有生命周期结束的时候,才会调用析构函数
void test()
{
//先创建的对象后释放
Test t1;
t1.print();
printf("分隔符\n");
Test t2;
t2.print();
}
#endif
/*
构造函数的分类
*/
#if 0
class Test
{
public:
//1、无参数构造函数
Test()
{
m_a = 0;
m_b = 0;
cout << "我是构造函数 被执行了" << endl;
}
//2、有参数构造函数
Test(int a)
{
cout << "我是一个参数的构造函数" << "a = " << a << endl;
m_a = a;
m_b = 0;
}
Test(int a, int b) //有参数构造函数 //3种方法
{
m_a = a;
m_b = b;
cout << "有参数构造函数" << endl;
}
//3、赋值构造函数(copy构造函数)
Test(const Test& obj)
{
cout << "copy构造函数也是构造函数 " << endl;
}
~Test()//析构函数
{
cout << "我是析构函数 被调用了" << endl;
}
public:
void printT()
{
cout << "普通成员函数" << endl;
}
private:
int m_a;
int m_b;
protected:
};
void test()
{
{
//调用无参数构造函数
Test t1;
//Test t1();// 错误的写法 warning C4930: “Test t1(void)”: 未调用原型函数(是否是有意用变量定义的?)
t1.printT();//使用的时候, Test t1()会出错
cout << "hello..." << endl;
}
//调用 调用有参数构造函数 3种方法
//1括号法
Test t1(1, 2);//调用参数构造函数 c++编译器 自动的 调用构造函数
//2 =号法 (3, 4) 是一个逗号表达式,此时需要注意类中仅有一个参数的成员函数
Test t2 = (3, 4); // = c+对等号符 功能增强 c++编译器 自动的 调用构造函数
Test t3 = (3, 4, 5); //调试看
Test t4 = 33; //调试看
//3 手动调用 构造函数 显示的调用 直接调用构造函数会产生匿名对象
//只会调用1次构造函数
//类名 对象名 = 构造函数名(11, 22)
Test t5 = Test(11, 22);//会产生匿名对象,此时匿名对象转正,不会被析构(匿名对象的去和留问题)直接调用有参构造函数完成t5对象的初始化
t1 = Test(11, 22); //会产生匿名对象,此时匿名对象被析构
t1 = t5;//把t5 copy 给 t1 是赋值操作
/*
Test t5 = Test(11, 22) //对象的初始化
t1 = t5; //对象的赋值
两条语句的 = 是不同的概念
对象的初始化 和对象的赋值 是两个不同的概念
*/
}
#endif
/*
copy构造函数的调用 时机1 时机2
*/
#if 0
class Test4
{
public:
Test4() //无参数构造函数
{
m_a = 0;
m_b = 0;
cout << "无参数构造函数" << endl;
}
Test4(int a)
{
m_a = a;
m_b = 0;
}
Test4(int a, int b) //有参数构造函数 //3种方法
{
m_a = a;
m_b = b;
cout << "有参数构造函数" << endl;
}
//赋值构造函数 (copy构造函数) 作用完成对象的初始化
Test4(const Test4& obj)
{
cout << "赋值构造函数 " << endl;
//注释掉 会乱码
#if 1
m_a = obj.m_a + 100;
m_b = obj.m_b + 100;
#endif
}
public:
void printT()
{
cout << "普通成员函数" << endl;
cout << "m_a = " << m_a << ", m_b = " << m_b << endl;
}
private:
int m_a;
int m_b;
};
void test()
{
//赋值构造函数 用一个对象初始化另一个对象
Test4 t1(10, 12);
Test4 t0(0, 2);
//赋值操作 和 初始化是两个不同的概念
//赋值 = 操作 不会调用拷贝构造函数
t0 = t1; //用t1 给 t0赋值
//第一种 调用拷贝构造函数的时机
Test4 t2 = t1; //用t1来初始化t2,调用t2这个对象的拷贝构造函数
t2.printT();
//第二种 调用拷贝构造函数的时机
Test4 t3(t1); //用t1来初始化t2
t3.printT();
}
#endif
/*
赋值构造函数的调用 时机3
*/
#if 0
class Location
{
public:
//构造函数
Location(int xx = 0, int yy = 0)
{
X = xx;
Y = yy;
cout << "Constructor Object." << endl;
}
//拷贝构造函数 完成对象的初始化
Location(const Location &obj)
{
X = obj.X;
Y = obj.Y;
}
~Location()
{
cout << X << "," << Y << " Object destroyed." << endl;
}
int GetX()
{
return X;
}
int GetY()
{
return Y;
}
private:
int X, Y;
};
//第三种 调用拷贝构造函数的时机
void f(Location p) //参数是一个元素
{
cout << p.GetX() << endl;
}
void test()
{
Location a(1, 2);
Location b = a; //第一种 调用拷贝构造函数的时机
/*
实参b去初始化形参p,c++编译器会调用形参 p 这个对象的copy构造函数,
由于p是局部变量,函数f()运行完毕,p对象的生命周期结束,会调用
p对象的析构函数。
先创建的对象后释放
*/
//b实参去初始化形参p,会调用copy构造函数
f(b); //第3种时机
cout << "b对象已经初始化完毕" << endl;
}
#endif
/*
赋值构造函数的调用 时机4
*/
#if 0
class Location
{
public:
//构造函数
Location(int xx = 0, int yy = 0)
{
X = xx;
Y = yy;
cout << "Constructor Object." << endl;
}
//拷贝构造函数 完成对象的初始化
Location(const Location &obj)
{
X = obj.X;
Y = obj.Y;
}
~Location()
{
cout << X << "," << Y << " Object destroyed." << endl;
}
int GetX() { return X; } int GetY() { return Y; }
private:
int X, Y;
};
//第4种 调用拷贝构造函数的时机
/*
g()函数 返回一个元素
结论 1:函数的返回值是一个元素(复杂类型的),返回一个新的匿名对象(所以会调用匿名对象
的 copy构造函数)。
结论 2:有关匿名对象的去和留
如果用匿名对象 初始化 另一个同类型的对象,匿名对象转化为有名对象,匿名对象不会被析构掉
如果用匿名对象 赋值给 另一个同类型的对象,匿名对象被析构
*/
Location g()
{
Location A(11, 22);//执行对象A的构造函数
return A;
/*
return A;
这条语句 首先会创建一个匿名对象,用对象A初始化匿名对象,执行匿名对象的copy构造函数,
然后对象A的生命周期结束了,会执行对象A的析构函数。g()返回一个匿名对象.
*/
}
void objplay2()
{
g();//g()返回一个匿名对象,如果匿名对象没人接,则会执行匿名对象的析构函数。
}
void objplay3()
{
//用匿名对象初始化m,此时c++编译器直接把匿名对象转成m(扶正),从匿名转成有名对象了m
Location m = g();
printf("匿名对象,被扶正,不会被析构掉\n");
cout << m.GetX() << endl;
}
void objplay4()
{
Location m2(1, 2);
m2 = g();//用匿名对象 赋值给 m2
printf("因为用匿名对象 = m2, 匿名对象被析构\n");
cout << m2.GetX() << endl;
}
void objplay5()
{
//用匿名对象 赋值给 m2
Location m1(1, 2);
m1 = Location(33, 44); //赋值操作 Location(33, 44)会产生匿名对象,匿名对象被析构
Location m2 = Location(55, 66);//初始化操作 Location(33, 44)会产生匿名对象,匿名对象转正,不会被析构
}
void objplay6()
{
Location(77, 88); //直接调用构造函数,会产生匿名对象,临时匿名对象的生命周期
}
void test()
{
//objplay2();
//objplay3();
//objplay4();
objplay5();
//objplay6();
}
#endif
/*
当类中定义了 拷贝 构造函数时, c++编译器不会提供无参数 构造函数
当类中定义了 有参数 构造函数时,c++编译器不会提供无参数 构造函数
在定义类时, 只要你写了构造函数,则必须要用
*/
#if 0
class Test
{
public:
//Test(const Test& obj) //copy构造函数 作用: 用一个对象初始化另外一个对象
//{
// a = obj.a + 100;
// b = obj.b + 100;
//}
/* Test(int _a, int _b)
{
;
}*/
/*Test()
{
}*/
void printT()
{
cout << "a:" << a << "b: " << b << endl;
}
protected:
private:
int a;
int b;
};
void test()
{
Test t1; //调用无参构造函数
cout << "hello..." << endl;
}
#endif
#if 0
class Name
{
public:
//构造函数
Name(const char *myp)
{
m_len = strlen(myp);
m_p = (char *)malloc(m_len + 1);
strcpy(m_p, myp);
}
//Name t2 = t1;
//解决方案:手工的编写拷贝构造函数 使用深copy
Name(const Name& obj)
{
m_len = obj.m_len;
m_p = (char *)malloc(m_len + 1);
strcpy(m_p, obj.m_p);
}
~Name()
{
if (m_p != NULL)
{
free(m_p);
m_p = NULL;//释放完内存要置空
m_len = 0;
}
}
protected:
private:
char *m_p;
int m_len;
};
void test()
{
Name t1("hello world"); //调用无参构造函数
//Name t2 = t1;//c++编译器提供的默认拷贝构造函数 是 浅拷贝,多次释放 同一块内存
//----------------------------------------------------------
Name t3("hhhh");
t3 = t1; // = 操作 把对象1的属性 拷贝给对象3 是浅拷贝,多次释放 同一块内存,还有一块内存未释放
//需要解决重载 = 操作符
cout << "hello..." << endl;
}
#endif
/*
构造函数的初始化列表
*/
#if 0
class A
{
public:
A(int _a)
{
a = _a;
cout << "构造函数" << " a = " << a << endl;
}
~A()
{
cout << "析构函数" << " a = " << a << endl;
}
protected:
private:
int a;
};
/*
1、构造函数的初始化列表
解决:在B类中 组合了一个 A类对象(A类设计了构造函数),
根据构造函数的调用规则,设计A的构造函数,必须要用;没有机会初始化A。
新的语法 Constructor::Contructor() : m1(v1), m2(v1,v2), m3(v3)
2、调用顺序
先执行组合对象的构造函数,如果组合对象有多个,按照定义顺序,而不是按照初始化列表的顺序
3、析构函数:和构造函数的调用顺序相反
4、初始化列表 用来给const 属性赋值
*/
class B
{
public:
B(int _b1, int _b2) :a1(1), a2(2), c(0)
{
}
B(int _b1, int _b2, int m, int n) :a1(m), a2(n), c(0)
{
b1 = _b1;
b2 = _b2;
cout << "B的构造函数" << endl;
}
~B()
{
cout << "B的析构函数" << endl;
}
protected:
private:
int b1;
int b2;
A a2; //组合对象
A a1; //
const int c;
};
void test()
{
A a1(10);
//B ojbB(1, 2);
//1 参数传递
B ojbB2(1, 2, 3, 4);
//2 调用顺序
cout << "hello..." << endl;
}
#endif
/*
构造中调用构造是危险的行为
*/
#if 0
class MyTest
{
public:
MyTest(int a, int b, int c)
{
this->a = a;
this->b = b;
this->c = c;
}
MyTest(int a, int b)
{
this->a = a;
this->b = b;
MyTest(a, b, 100); //直接调用构造函数,产生新的匿名对象,没人接,会直接调用匿名对象的析构函数。 跟t1对象没有半毛线关系
}
~MyTest()
{
printf("MyTest~:%d, %d, %d\n", a, b, c);
}
protected:
private:
int a;
int b;
int c;
public:
int getC() const { return c; }
void setC(int val) { c = val; }
};
void test()
{
MyTest t1(1, 2);
printf("-----c:%d", t1.getC()); //请问c的值是?
}
#endif
/*
new和delete的基本使用,是c++的操作符
*/
#if 0
class Test
{
public:
Test(int _a)
{
a = _a;
cout << "构造函数执行" << endl;
}
~Test()
{
cout << "析构函数执行" << endl;
}
protected:
private:
int a;
};
void test()
{
int *p = new int;
*p = 20;
printf("*p = %d\n", *p);
delete p;
int *p1 = new int(21);//定义并初始化
printf("*p1 = %d\n", *p1);
delete p1;
int *pArray = new int[10];
pArray[0] = 10;
delete[]pArray;
char *str = new char[25];
strcpy(str, "12345");
printf("str = %s\n", str);
delete[]str;
//对象的操作
Test *pT1 = (Test *)malloc(sizeof(Test));
free(pT1);
Test *pT2 = new Test(10); //new能执行类型构造函数
delete(pT2); //delete操作符 能执行类的析构函数
printf("---------------------------\n");
//深入分析
//malloc 用delete释放
Test *pT3 = (Test *)malloc(sizeof(Test));
delete(pT3);
//new 用free释放
Test *pT4 = new Test(10); //new能执行类型构造函数
free(pT4); //delete操作符 能执行类的析构函数
/*
new不但可以分配内存,还可以初始化对象
malloc不会调用类的构造函数
Free不会调用类的析构函数
*/
}
#endif
/*
static 静态成员函数 和静态成员变量
*/
#if 0
class BB
{
int c0;//默认是private
public:
static int d; //使用公有静态数据成员
void printC()//成员函数访问静态成员变量
{
cout << "c = " << c << endl;
}
void addC()
{
c = c + 1;
}
//静态成员函数
//c++编译器无法确认是b1.a b2.a b3.a
static void getC()
{
cout << "c = " << c << endl;
//cout << "a = " << a << endl;//error C2597: 对非静态成员“BB::a”的非法引用 ,因为静态成员变量属于整个类的,分不清楚普通成员变量是哪个具体对象的属性
/*
静态函数中 不能使用 普通成员变量 普通成员函数 .
静态成员函数数冠以关键字static
静态成员函数提供不依赖于类数据结构的共同操作,它没有this指针
在类外调用静态成员函数用 “类名 :: ”作限定词,或通过对象调用
静态成员函数属于整个类的
static修饰的变量,是属于类,所有的对象都能共享使用
*/
}
protected:
private:
int a;
int b;
static int c;//静态成员变量
};
/*
如果不写静态函数的定义,当不用类定义对象时候,编译不会报错,只用定义类的对象时候
不写的话才会报错
*/
int BB::c = 10; //必须在类外声明与定义静态数据成员
int BB::d = 11;
//static 修饰的变量,属于类,所有的对象都能共享用
void test()
{
BB b1, b2, b3;
b1.printC(); //10
b2.addC(); //11
b3.printC(); //11
b3.getC();
//访问公有静态成员方法1
cout << "BB::d = " << BB::d << endl;
//访问公有静态成员方法2
cout << "b1.d = " << b1.d << endl;
//静态成员函数 调用方法
b3.getC(); //用对象
BB::getC(); //类::
}
#endif
/*
1)C++类对象中的成员变量和成员函数是分开存储的
成员变量:
普通成员变量:存储于对象中,与struct变量有相同的内存布局和字节对齐方式
静态成员变量:存储于全局数据区中
成员函数:存储于代码段中。
1、C++类对象中的成员变量和成员函数是分开存储的。C语言中的内存四区模型仍然有效!
2、C++中类的普通成员函数都隐式包含一个指向当前对象的this指针。
3、静态成员函数、成员变量属于类
静态成员函数与普通成员函数的区别
静态成员函数不包含指向具体对象的指针
普通成员函数包含一个指向具体对象的指针
*/
#if 0
class C1
{
public:
int i; //4
int j; //4
int k; //4
protected:
private:
}; //12
class C2
{
public:
int i;
int j;
int k;
static int m; //4
public:
int getK() const { return k; } //4
void setK(int val) { k = val; } //4
protected:
private:
}; //24 16 12(铁钉的不对)
struct S1
{
int i;
int j;
int k;
}; //
struct S2
{
int i;
int j;
int k;
static int m;
}; //
void test()
{
printf("c1:%d \n", sizeof(C1));
printf("c2:%d \n", sizeof(C2));
printf("s1:%d \n", sizeof(S1));
printf("s2:%d \n", sizeof(S2));
}
#endif
//this指针和const
#if 0
class A
{
public:
A(int a, int b)// ====》A( A*pThis, int a, int b);
{
this->a = a;
this->b = b;
}
void printA()
{
cout << "a = " << a << endl;
cout << "b = " << this->b << endl;
}
void opVar1(int a, int b)//==> void opVar1(A * const pThis, int a, int b)
{
a = 100;
this->a = 80;
}
/*
const写在什么位置 没有关系
const修饰的是this指针所指向的内存空间,修饰的是this指针
指针变量与指针所指向的内存空间 是两个不同的概念
*/
void opVar2(int a, int b) const //==>void opVar1(const A * const pThis,int a, int b)
{
a = 100;
//this->a = 80;//err
}
protected:
private:
int a;
int b;
};
void test()
{
A a1(1, 2);
a1.printA();//==> printA(&a1);
}
#endif
/*
成员函数与全局函数的转换
*/
#if 0
class Test
{
public:
Test(int a = 0, int b = 0)
{
this->a = a;
this->b = b;
}
/*
t4 = t1.TestAdd(t2);
返回一个引用 相当于返回自身
返回t1这个元素 this 就是&t1, this 是常指针
*/
Test TestAdd(Test &obj)//函数返回元素
{
Test tmp(this->a + obj.a, this->b + obj.b);
return tmp;//返回一个匿名对象
}
// t1.TestAdd(t2);函数返回引用
void TestAdd2(Test &obj)
{
this->a = this->a + obj.a;
this->b = this->b + obj.b;
//return *this;//把 *(&t1) 又回到了 t1元素
}
~Test()
{
cout << "a:" << a << " b: " << b;
cout << "析构函数自动被调用" << endl;
}
void printT()
{
cout << "a:" << a << " b: " << b << endl;
}
public:
int a;
int b;
};
//成员函数 转成全局函数 多了一个参数
void printT(Test *pT)
{
cout << "a" << pT->a << "b" << pT->b << endl;
}
//全局函数方法
//全局函数 转成 成员函数 少了一个参数
Test TestAdd(Test &t1, Test &t2)
{
Test tmp;
tmp.a = t1.a + t2.a;
tmp.b = t1.b + t2.b;
return tmp;
}
void test()
{
Test t1(1, 2);
Test t2(3, 4);
Test t3;//偷懒的写法是在构造函数中直接使用默认参数
//全局函数方法
t3 = TestAdd(t1, t2);
t3.printT();
//成员函数法
{
//先把测试案例写出来 t4 = t1 + t2
Test t4 = t1.TestAdd(t2);//匿名对象直接转化为t4
t4.printT();
Test t5;
t5 = t1.TestAdd(t2);//匿名对象复制给t5
//t1 = t1+ t2
t1.TestAdd2(t2);
t1.printT();
}
}
#endif
/*
友元函数和友元类
*/
#if 0
class A
{
public:
friend class B;//B类是A类的好朋友, 在B类中可以访问A类的所有成员
//1 声明的位置 和 public private没有关系
//2 函数modifyA 是 类A的好朋友
friend void modifyA(A *pA, int _a);
A(int a = 0, int b = 0)
{
this->a = a;
this->b = b;
}
int getA()
{
return this->a;
}
private:
int a;
int b;
};
//友元函数是全局函数,没有this指针 可以修改类的私有成员 注意函数原型的第一个参数
void modifyA(A *pA, int _a)
{
pA->a = _a;
}
class B
{
public:
void Set(int a)
{
Aobject.a = a;
}
void printB()
{
cout << Aobject.a << endl;
}
private:
A Aobject;//类A定义
};
//为什么设计友元类函数
// 1.java--->1.class(字节码) ==》反射机制分析1.class 找到类对象。直接修改类的私有属性。。。
//反射机制 成为一种标准。。。。jdk ...sun 做成标准 。。。jdk 的 api函数中有体现
//AOP
//2 1.cpp===>汇编
// 预编译 编译 连接 生成 。。gcc -E //gcc -s -
//gcc -o 1.exe 1.c
// 汇编往会找。。。。很难。。。。
//3 开了一个后门 。。。friend
/*
gcc -E hello.c -o hello.i(预处理)
gcc -S hello.i -o hello.s(编译)
gcc -c hello.s -o hello.o(汇编)
gcc hello.o -o hello(链接)
以上四个步骤,可合成一个步骤
gcc hello.c -o hello(直接编译链接成可执行目标文件)
gcc -c hello.c或gcc -c hello.c -o hello.o(编译生成可重定位目标文件)
*/
void test()
{
A a1(1, 2);
cout << a1.getA() << endl;
modifyA(&a1, 300);
cout << a1.getA() << endl;
B b1;
b1.Set(600);
b1.printB();
}
#endif
/*
运算符重载的技术推演
*/
#if 0
class Complex
{
public:
friend Complex myAdd(Complex &c1, Complex &c2);
friend Complex operator+(Complex &c1, Complex &c2);
Complex(int a = 0, int b = 0)
{
this->a = a;
this->b = b;
}
void printCom()
{
cout << a << " + " << b << "i" << endl;
}
private:
int a;
int b;
};
//1 定义全局函数
Complex myAdd(Complex &c1, Complex &c2)
{
Complex tmp(c1.a + c2.a, c1.b + c2.b);
return tmp;
}
//函数名升级
Complex operator+(Complex &c1, Complex &c2)
{
Complex tmp(c1.a + c2.a, c1.b + c2.b);
return tmp;
}
/*
全局函数、类成员函数方法实现运算符重载步骤
1)要承认操作符重载是一个函数,写出函数名称
2)根据操作数,写出函数参数
3)根据业务,完善函数返回值(看函数是返回引用 还是指针 元素),及实现函数业务
*/
void test()
{
int a = 0, b = 0;
int c;
c = a + b;//1 基础数据类型 编译器已经知道了. 如何运算
Complex c1(1, 2), c2(3, 4);
Complex c3;
//2 类 也是一种数据类型 用户自定义数据类型 C++编译器 是不知道如何进行运算
//c3 = c1 + c2
//3 c++编译器应该给我们程序员提供一种机制 ...
//让自定义数据类型 有机会 进行 运算符操作 ====> 运算符重载机制
//4 运算符重载机制
//步骤1
Complex c4 = myAdd(c1, c2);
c4.printCom();
//步骤2 Complex c4 = c1 + c2
Complex c5 = operator+(c1, c2);
c5.printCom();
//步骤3
Complex c6 = c1 + c2;
c6.printCom();
//总结 运算符重载的本质 是函数调用
}
#endif
/*
运算符重载 - + -- ++
*/
#if 0
class Complex
{
public:
//前置++
friend Complex& operator++(Complex &c1);
//后置++
friend Complex operator++(Complex &c1, int);//使用占位符
//成员函数 重载 -运算符
Complex operator-(Complex &c2)
{
cout << "c2.a" << c2.a << " c2.b" << c2.b << endl;
//Complex tmp = (this->a - c2.a, this->b - c2.b);//是一个逗号表达式
Complex tmp(this->a - c2.a, this->b - c2.b);
return tmp;
}
//前置--
Complex& operator--()
{
this->a--;
this->b--;
return *this;
}
//后置--
Complex operator--(int)
{
Complex tmp = *this;
this->a--;
this->b--;
return tmp;
}
Complex(int a = 0, int b = 0)
{
this->a = a;
this->b = b;
}
void printCom()
{
cout << a << " + " << b << "i" << endl;
}
private:
int a;
int b;
};
//前置++ 全局函数
Complex& operator++(Complex &c1)
{
c1.a++;
c1.b++;
return c1;
}
//后置++ 全局函数
Complex operator++(Complex &c1, int)
{
//先使用 后加1
Complex tmp = c1;
c1.a++;
c1.b++;
return tmp;
}
void test()
{
Complex c1(1, 2), c2(3, 4);
//1 成员函数 法 实现 -运算符重载
//
//c1.operator-(c2);
//Complex operator-(Complex &c2);
Complex c3 = c1 - c2;
c3.printCom();
//重载 前置++ 全局函数
++c1;
c1.printCom();
//重载 前置-- 成员函数
--c1;
c1.printCom();
//重载后置++ 全局函数
c1++;
c1.printCom();
//重载后置-- 成员函数
c1--;
c1.printCom();
}
#endif
/*
+ - ++ -- << 重载操作
*/
#if 0
class Complex
{
public:
//friend void operator<<(ostream &out, Complex &obj);
friend ostream& operator<<(ostream &out, Complex &obj);
Complex(int a = 0, int b = 0)
{
this->a = a;
this->b = b;
}
//c3 = c1 + c2
Complex operator+(Complex &obj)
{
Complex tmp(this->a + obj.a, this->b + obj.b);
return tmp;
}
//c1 = c1 - c2
Complex& operator-(Complex &obj)
{
this->a = this->a - obj.a;
this->b = this->b - obj.b;
return *this;
}
//++c1 使用前先将自身加1
Complex& operator++()
{
this->a++;
this->b++;
return *this;
}
//c1++
Complex operator++(int)
{
Complex tmp = *this;
this->a++;
this->b++;
return tmp;
}
//--c1
Complex& operator--()
{
this->a--;
this->b--;
return *this;
}
//c1--
Complex operator--(int)//使用占位符,c++编译器则能够区分是后置--
{
Complex tmp = *this;
this->a--;
this->b--;
return tmp;
}
void print()
{
cout << a << " + " << b << "i" << endl;
}
private:
int a;
int b;
};
//void operator<<(ostream &out, Complex &obj)
//{
// out << obj.a << " + " << obj.b << endl;
//}
// << 必须用友元函数 因为取不到 ostream 类的实现
ostream& operator<<(ostream &out, Complex &obj)
{
out << obj.a << " + " << obj.b << endl;
return out;
}
void test()
{
Complex c1(1, 2), c2(3, 4);
Complex c3 = c1 + c2;
c3.print();
c1 = c1 - c2;
c1.print();
++c1;
c1.print();
c1++;
c1.print();
--c1;
c1.print();
c1--;
c1.print();
int a = 10;
cout << a << endl;
//重载<< 只能用友元函数
//2 ostream 类中 添加 成员函数 .operator<<
//ostream
//cout.operator<<(c1);
cout << c1;
//函数返回值当左值,需要返回一个引用
//cout.operator<<(c1).operator<<("hello")
//void.operator<<("aaddddd");
//<< 从左到右
cout << c1 << "hello" << endl;
}
#endif
/*
重载 = 操作 (赋值)
*/
#if 0
class Name
{
public:
Name(const char *p)
{
this->m_len = strlen(p) + 1;
this->m_p = new char[m_len];
strcpy(m_p, p);
}
//Name n2 = n1; 深copy
Name(const Name& obj)
{
m_len = obj.m_len;
m_p = new char[m_len];
strcpy(m_p, obj.m_p);
}
//n3 = n1; 赋值操作是浅拷贝,需要重载=
Name& operator=(Name& obj)
{
//1 释放 n3 旧的内存
if (this->m_p != NULL)
{
delete[]m_p;
m_p = NULL;
m_len = 0;
}
//2 重新根据n1 给n3分配内存
m_len = obj.m_len;
m_p = new char[m_len];
//3 复制
strcpy(m_p, obj.m_p);
return *this;
}
~Name()
{
if (m_p != NULL)
{
delete[]m_p;
m_p = NULL;
m_len = 0;
}
}
private:
int m_len;
char* m_p;
};
void test()
{
Name n1("hello");
Name n2 = n1; //初始化 调用拷贝构造函数
Name n3("haha");
//n3.operator(n1)
n3 = n1; //赋值操作也是浅拷贝, 需要 =操作符重载
Name n4("4444");
Name n5("55555");
Name n6("666666");
//= 自右向左
//n5.operator(n6)
//n4 = void
//void.operator(n5)
n4 = n5 = n6;//链式编程
}
#endif
/*
重载()
*/
#if 0
class F
{
public:
//()重载
int operator() (int a, int b)
{
return a*a + b*b;
}
};
class F2
{
public:
int Menfuc(int a, int b)
{
return a*a + b*b;
}
};
void test()
{
F f;
// ()重载
f(2, 4);//要么是成员函数初始化,要么是()重载
F2 f2;
//函数调用
f2.Menfuc(2, 4);
}
#endif
/*
理论知识:
1)&&和||是C++中非常特殊的操作符
2)&&和||内置实现了短路规则
3)操作符重载是靠函数重载来完成的
4)操作数作为函数参数传递
5)C++的函数参数都会被求值,无法实现短路规则
结论:
&&和||可以实现运算符重载,但无法实现短路规则
*/
#if 0
class Test
{
int i;
public:
Test(int i)
{
this->i = i;
}
Test operator+ (const Test& obj)
{
Test ret(0);
cout << "执行+号重载函数" << endl;
ret.i = i + obj.i;
return ret;
}
// 重载 &&
bool operator&& (const Test& obj)
{
cout << "执行&&重载函数" << endl;
return i && obj.i;
}
};
// && 从左向右
void test()
{
int a1 = 0;
int a2 = 1;
cout << "注意:&&操作符的结合顺序是从左向右" << endl;
//短路规则 a1 为0,则a1 + a2 不执行
if (a1 && (a1 + a2))
{
cout << "有一个是假,则不在执行下一个表达式的计算" << endl;
}
//测试
Test t1 = 0;
Test t2 = 1;
//if( t1 && (t1 + t2) )
//t1 && t1.operator+(t2)
// t1.operator&&( t1.operator+(t2) )
//1 && || 重载他们 不会产生短路效果
if ((t1 + t2) && t1)
{
//t1.operator+(t2) && t1;
//(t1.operator+(t2)).operator&&(t1);
cout << "两个函数都被执行了,而且是先执行了+" << endl;
}
//2 && 运算符的结合性
// 两个逻辑与运算符 在一块的时候, 才去谈 运算符的结合性
// 从左到右 (t1 + t2) && t1 ; 运算结果 && t2)
//if( (t1 + t2) && t1 && t2)
{
//t1.operator+(t2) && t1;
//(t1.operator+(t2)).operator&&(t1);
cout << "两个函数都被执行了,而且是先执行了+" << endl;
}
}
#endif
/*
继承的基本语法
*/
#if 0
class Parent
{
public:
void print()
{
a = 0;
b = 0;
cout << "a" << a << endl;
cout << "b" << b << endl;
}
int b;
protected:
private:
int a;
};
class Child : public Parent
{
public:
protected:
private:
int c;
};
void test()
{
Child c1;
c1.b = 10;
//c1.c = 10;
c1.print();
}
#endif
/*
单个类的访问控制
public: 修饰的成员变量 方法 在类的内部、类的外部都能使用
protected: 修饰的成员变量 方法 在类的内部使用, 在继承的子类中使用;其他类的外部不能使用
private: 修饰的成员变量 方法 只能在类的内部使用, 不能在类的外部中使用
派生类访问控制结论:
1 protected 关键字 修饰的成员变量和成员函数, 是为了在家族中使用,是为了继承
2 项目中开发一般用public
*/
#if 0
class Parent
{
public:
int a;
protected:
int b;
private:
int c;
};
//public 控制
class Child : public Parent
{
public:
void useVar()
{
a = 0;//ok
b = 0;//ok
//c = 0;//err
}
protected:
private:
};
//private
class Child2 : private Parent
{
public:
void useVar()
{
a = 0;//ok
b = 0;//ok
//c = 0;//err
}
protected:
private:
};
//protected
class Child3 : protected Parent
{
public:
void useVar()
{
a = 0;//ok
b = 0;//ok
//c = 0;//err
}
protected:
private:
};
/*
C++中的继承方式(public、private、protected)会影响子类的对外访问属性
判断某一句话,能否被访问
1)看调用语句,这句话写在子类的内部、外部
2)看子类如何从父类继承(public、private、protected)
3)看父类中的访问级别(public、private、protected)
*/
void test()
{
Parent t1, t2;
t1.a = 10;//ok
//t1.b = 10;//err
//t1.c = 10;//err
Child2 c2;
//c2.a = 10;//err
//c2.b = 10;//err
//c2.c = 10;//err
Child3 c3;
//c3.a = 10;//err
//c3.b = 10;//err
//c3.c = 10;//err
}
#endif
/*
访问控制原则 综合练习
*/
#if 0
class A
{
private:
int a;
protected:
int b;
public:
int c;
A()
{
a = 0; b = 0; c = 0;
}
void set(int a, int b, int c)
{
this->a = a; this->b = b; this->c = c;
}
};
class B : public A
{
public:
void print()
{
//cout<<"a = "<<a; //err
cout << "b = " << b; //ok
cout << "c = " << endl; //ok
}
};
class C : protected A
{
public:
void print()
{
//cout<<"a = "<<a; //err
cout << "b = " << b; //ok
cout << "c = " << endl; //ok
}
};
class D : private A
{
public:
void print()
{
//cout<<"a = "<<a; //err
cout << "b = " << b << endl; //ok
cout << "c = " << c << endl; //ok
}
};
void test()
{
A aa;
B bb;
C cc;
D dd;
aa.c = 100; // ok
bb.c = 100; // ok
//cc.c = 100; // err
//dd.c = 100; // err
aa.set(1, 2, 3); //ok
bb.set(10, 20, 30); //ok
//cc.set(40, 50, 60); //err
//dd.set(70, 80, 90); //err
bb.print(); //ok
cc.print(); //ok
dd.print(); //ok
}
#endif
/*
类型兼容性原则
*/
#if 0
/*
兼容规则中所指的替代包括以下情况:
子类对象可以当作父类对象使用
子类对象可以直接 赋值 给父类对象
子类对象可以直接 初始化 父类对象
父类指针可以直接指向子类对象
父类引用可以直接引用子类对象
*/
class Parent
{
public:
void printP()
{
cout << "我是爹。。。" << endl;
}
Parent()
{
cout << "Parent 构造函数" << endl;
}
Parent(const Parent &obj)
{
cout << "copy 构造函数" << endl;
}
private:
int a;
};
class child : public Parent
{
public:
void printC()
{
cout << "我是儿子" << endl;
}
protected:
private:
int c;
};
void howToPrint(Parent *base)
{
base->printP();//父类的 成员函数
}
void howToPrint2(Parent &base)
{
base.printP();//父类的 成员函数
}
void test()
{
Parent p1;
p1.printP();
child c1;
c1.printC();
c1.printP();
//赋值兼容性原则
//1-1 基类指针(引用)指向子类对象
Parent *p = NULL;
p = &c1;
p->printP();
//1-2 指针做函数参数
howToPrint(&p1);
howToPrint(&c1); //父类指针可以直接指向子类对象
//1-3引用做函数参数
howToPrint2(p1);
howToPrint2(c1); //父类引用可以直接引用子类对象
//子类对象 初始化父类对象 子类是一种特殊的父类
Parent p3 = c1;
p1 = c1; //子类对象可以直接 赋值 给父类对象
}
#endif
/*
继承中的构造和析构
结论:
先调用 父类构造函数 在调用子类构造函数
析构的顺序 和构造相反
1 子类对象在创建时会首先调用父类的构造函数
2 父类构造函数执行结束后,执行子类构造函数
3 当父类的构造函数有参数时,需要在子类的初始化列表中显示调用
4 析构函数调用的顺序与构造函数相反
*/
#if 0
class Parent
{
public:
//构造函数,写了就必须使用
Parent(int a, int b)
{
cout << "父类构造函数。。。" << endl;
}
~Parent()
{
cout << "父类析构函数。。。" << endl;
}
void printP(int a, int b)
{
this->a = a;
this->b = b;
cout << "我是爹..." << endl;
}
private:
int a;
int b;
};
class child : public Parent
{
public:
//从父类中继承,必须提供有参构造函数,发现没有机会提供有参构造函数。因此
//需要使用构造函数的初始化列表
//child(int c)
child(int a, int b, int c) :Parent(11, 22)
{
this->c = c;
cout << "子类构造函数。。" << endl;
}
~child()
{
cout << "子类析构。。。" << endl;
}
void printC()
{
cout << "我是儿子" << endl;
}
protected:
private:
int c;
};
void playObj()
{
child c1(1, 2, 3);
}
void test()
{
Parent p(1, 2);
playObj();
}
#endif
/*
继承和组合混搭下的构造和析构
单步执行 查看调用顺序
结论:
先构造父类, 再构造成员变量,最后调用自己
先析构自己, 在析构成员变量,最后析构父类
*/
#if 0
class Object
{
public:
Object(int a, int b)
{
this->a = a;
this->b = b;
cout << "object构造函数 执行 " << "a" << a << " b " << b << endl;
}
~Object()
{
cout << "object析构函数 \n";
}
protected:
int a;
int b;
};
class Parent :public Object
{
public:
//继承了Object类,构造函数,写了就必须使用
Parent(char *p) :Object(1, 2)
{
this->p = p;
cout << "父类构造函数。。。" << p << endl;
}
~Parent()
{
cout << "父类析构函数。。。" << p << endl;
}
void printP(int a, int b)
{
cout << "我是爹..." << endl;
}
protected:
char *p;
};
class child : public Parent
{
public:
//从父类中继承,必须提供有参构造函数,发现没有机会提供有参构造函数。因此
//需要使用构造函数的初始化列表
//child(int c)
child(char *p) :Parent(p), obj1(3, 4), obj2(5, 6)
{
this->myp = p;
cout << "子类的构造函数。。" << endl;
}
~child()
{
cout << "子类析构。。。" << myp << endl;
}
void printC()
{
cout << "我是儿子" << endl;
}
protected:
char *myp;
Object obj2;
Object obj1;
};
void test()
{
child c1("继承测试");
}
#endif
/*
继承中的同名成员变量、同名成员函数
1 当子类成员变量与父类成员变量同名时,子类依然从父类继承同名成员
2 在子类中通过作用域分辨符::进行同名成员区分(在派生类中使用基类
的同名成员,显式地使用类名限定符)
3 同名成员存储在内存中的不同位置
结论:
基类成员的作用域延伸到所有的派生类
派生类的重名成员屏蔽基类的同名成员
派生类屏蔽基类同名成员函数,调用自身的成员函数
*/
#if 0
class A
{
public:
int a;
int b;
public:
void get()
{
cout << "b = " << b << endl;
}
void print()
{
cout << "AAAA" << endl;
}
protected:
private:
};
class B : public A
{
public:
int b;
int c;
void get_child()
{
cout << "b = " << b << endl;
}
void print()
{
cout << "BBBB" << endl;
}
protected:
private:
};
void test()
{
B b1;
b1.b = 1;
b1.get_child();
b1.A::b = 100;//修改父类的b
b1.B::b = 12; //修改子类的b , 默认情况是B
b1.get();
b1.print();
b1.A::print();//调用父类的同名成员函数
}
#endif
/*
继承中的static 关键字
1、基类定义的静态成员,将被所有的派生类共享。
2、static 关键字修饰的成员变量或者函数 遵守 派生类的访问控制规则
3、static易犯错误(不但要初始化,更重要的显示的告诉编译器分配内存)
构造函数默认为 private!!!
*/
#if 0
class A
{
//注意 类默认下的函数 是私有的
/*A()
{
cout << "A的构造函数" << endl;
}*/
public:
A()
{
cout << "A的构造函数" << endl;
}
public:
static int a;// static 关键字
int b;
public:
void get()
{
cout << "b = " << b << endl;
}
void print()
{
cout << "AAAAA " << endl;
}
};
/*
不是简单的赋值 更重要的是 要告诉C++编译器, 你要给我分配内存,
我在继承中用到了a ,不然会报错
*/
int A::a = 100;
class B : private A
{
public:
int b;
int c;
public:
void get_child()
{
cout << "b " << b << endl;
cout << a << endl;
}
void print()
{
cout << "BBBB " << endl;
}
protected:
private:
};
void test()
{
B b1;
//b1.a = 200;//err a虽然是static,但是私有继承父类
b1.get_child();
A a1;
a1.print();
}
#endif
/*
多继承的语法
*/
#if 0
class Base1
{
public:
//构造函数写了,就要用
Base1(int b1)
{
this->b1 = b1;
}
void printB1()
{
cout << "b1:" << b1 << endl;
}
protected:
private:
int b1;
};
class Base2
{
public:
Base2(int b2)
{
this->b2 = b2;
}
void printB2()
{
cout << "b2:" << b2 << endl;
}
protected:
private:
int b2;
};
class B : public Base1, public Base2
{
public:
//父类的构造函数写了,就必须用初始化列表初始化
B(int b1, int b2, int c) :Base1(b1), Base2(b2)
{
this->c = c;
cout << b1 << " " << b2 << c << endl;
}
void printC()
{
cout << "c:" << c << endl;
}
protected:
private:
int c;
};
void test()
{
B b1(1, 2, 3);
b1.printC();
b1.printB1();
b1.printB2();
}
#endif
/*
1、虚继承 让老祖宗的构造函数只执行一次。
若不加virtual, 则会执行2次老祖宗的构造函数虚继承,只会解决有共同老祖宗的这种问题
2、子类有两个父类且这两个父类都有相同的成员变量,则加上virtual也不能解决
结论:
多继承始终存在问题
*/
#if 0
class B
{
public:
int b;
B()
{
cout << "老祖宗" << endl;
}
protected:
private:
};
class B1 : virtual public B
{
public:
int b1;
};
class B2 : virtual public B
{
public:
int b2;
};
class C : public B1, public B2
{
public:
int c;
};
void test()
{
C c1;
c1.b1 = 100;
c1.b2 = 200;
c1.c = 300;
//c1.b = 500;//继承的二义性 ,用虚继承解决此种场景
c1.B1::b = 500;
c1.B2::b = 500;
}
#endif
/*
多继承的另一种场景 用virtual也不能解决
2、子类有两个父类且这两个父类都有相同的成员变量,则加上virtual也不能解决
*/
#if 0
class B1
{
public:
int b;
};
class B2
{
public:
int b;
};
class C : virtual public B1, virtual public B2
{
public:
int c;
};
void test()
{
C c1;
//c1.b = 500;//继承的二义性 即使加上virtual也不能解决
//只能这么解决
c1.B1::b = 500;
c1.B2::b = 500;
}
#endif
/*
多继承抛砖 测试加上virtual关键字后类的大小
*/
#if 0
class B
{
public:
int b;
};
//加上virtual关键字后,则类中含有vptr指针,指向虚函数表的入口位置
class B1 :virtual public B
{
public:
int b1;
};
class B2 : public B
{
public:
int b2;
};
class C : public B1, public B2
{
public:
int c;
};
void test()
{
cout << sizeof(B) << endl; //4
cout << sizeof(B1) << endl; //12 //加上virtual以后 , C++编译器会在给变量偷偷增加属性
cout << sizeof(B2) << endl; //8
}
#endif
/*
类型兼容性原则遇上函数重写
父类和子类有相同的成员函数,如果不写virtual,则子类初始化(或者赋值)父类
时,只执行父类的同名函数,不满足需求。
*/
#if 0
class Parent
{
public:
Parent(int a)
{
this->a = a;
cout << "Parent a" << a << endl;
}
//在基类中加上virtual关键字后,子类可加可不加(默认是加上了),最好显示加上表明重写了
virtual void print() //子类的和父类的函数名字一样
{
cout << "Parent 打印 a:" << a << endl;
}
protected:
private:
int a;
};
class Child : public Parent
{
public:
Child(int b) : Parent(10)
{
this->b = b;
cout << "Child b" << b << endl;
}
virtual void print() //virtual 父类写了virtual,子类可写 可不写
{
cout << "Child 打印 b:" << b << endl;
}
protected:
private:
int b;
};
void howToPrint(Parent *base)
{
base->print(); //一种调用语句 有多种表现形态...
}
void howToPrint2(Parent &base)
{
base.print();
}
void test()
{
Parent *base = NULL;
Parent p1(20);
Child c1(30);
base = &p1;
base->print();//执行父类的打印函数
base = &c1;//子类对象初始化基类
base->print();//执行谁的函数 ? //面向对象新需求 加上了virtual,则有多态发生
{
Parent &base2 = p1;
base2.print();
Parent &base3 = c1; //base3是c1 的别名
base3.print();
}
//函数调用
howToPrint(&p1);
howToPrint(&c1);//多态
howToPrint2(p1);
howToPrint2(c1);//多态
}
#endif
/*
函数重写
1、在子类中定义与父类中原型相同的函数
2、函数重写只发生在父类与子类之间
3、父类中被重写的函数依然会继承给子类
4、默认情况下子类中重写的函数将隐藏父类中的函数
5、通过作用域分辨符::可以访问到父类中被隐藏的函数
多态案例、成立的三个条件、本质
多态思想
面向对象三大概念
封装:突破c函数的概念,用类做函数参数的时候,可以使用对象的属性和对象的方法
继承:A B代码复用
多态:可以使用未来
实现多态的三个条件
c语言 间接赋值 是指针存在的最大意义,是c语言特有的现象
(1 定义两个变量 2 建立关联 3 *p在被调用函数中去间接修改实参的值)
实现多态的三个条件:
1、要有继承
2、要有虚函数重写
3、用父类指针(父类引用)指向子类对象
*/
#if 0
class HeroFighter
{
public:
virtual int power()//C++会对这个函数特殊处理
{
return 10;
}
};
class EnemyFighter
{
public:
int attack()
{
return 15;
}
};
class AdvHeroFighter : public HeroFighter
{
public:
virtual int power()
{
return 20;
}
};
class AdvAdvHeroFighter : public HeroFighter
{
public:
virtual int power()
{
return 30;
}
};
/*
多态威力
PlayObj 给对象搭建舞台 看成一个框架!!!
*/
void playObj(HeroFighter *hf, EnemyFighter *ef)
{
/*
不写virtual关键字是静态联编 c++编译器根据HeroFighter 类型,去执行这个
类型的power函数,在编译器编译阶段就已经决定了函数的调用,函数重载就是
静态联编
动态联编:迟绑定 在运行的时候,根据具体对象(具体的类型),执行不同对象
的函数,表现成多态。
*/
if (hf->power() > ef->attack())//hf->power()函数调用会有多态发生
{
cout << "主角win" << endl;
}
else
{
cout << "主角挂掉" << endl;
}
}
void test()
{
HeroFighter hf;
AdvHeroFighter Advhf;
EnemyFighter ef;
AdvAdvHeroFighter advadvhf;
playObj(&hf, &ef);
playObj(&Advhf, &ef);
//playObj 这个框架能够把后来人写的代码给调用起来
playObj(&advadvhf, &ef);
}
#endif
/*
虚析构函数应用场景:通过父类指针释放所有的子类对象
1、c++中不能声明虚构造函数。派生类对象撤销时,先调用派生类的析构,然后
再调用基类的析构。
delete无名对象时候,只执行基类的析构函数,而不执行派生类的析构函数原因
是调用析构函数时,采用了静态联编方式,只调用了基类的析构函数。
3、基类的析构函数加上virtual,虽然派生类和基类的析构函数名字不同,但由
该基类所派生的所有派生类的析构函数也都自动成为虚函数。
4、虚析构函数采用了动态联编,实现了运行时的多态。
*/
#if 0
class A
{
public:
A()
{
p = new char[20];
strcpy(p, "obja");
cout << "A()" << endl;
}
//在父类中加上 virtual 关键字 虚析构函数,表现出多态
virtual ~A()
{
delete[] p;
cout << "~A()" << endl;
}
private:
char *p;
};
class B : public A
{
public:
B()
{
p = new char[20];
strcpy(p, "objb");
cout << "B()" << endl;
}
~B()
{
delete[] p;
cout << "~B()" << endl;
}
private:
char *p;
};
class C : public B
{
public:
C()
{
p = new char[20];
strcpy(p, "objc");
cout << "C()" << endl;
}
~C()
{
delete[] p;
cout << "~C()" << endl;
}
private:
char *p;
};
/*
只执行了 父类的析构函数
通过父类指针 把所有的子类对象的析构函数 都执行一遍
通过父类指针 释放所有的子类资源
*/
void howtodelete(A *base)
{
//基类中的析构不加virtual,则这句话不会表现成多态 这种属性
delete base;
}
void howtodelete2(B *base)
{
delete base;
}
void test()
{
//使用new会调用对象的构造函数
C *myC = new C;
delete myC;//这种场景下直接通过子类对象释放所有资源,则不需要写virtual
printf("-----------------\n");
C *myC0 = new C;
//类型兼容性原则
//通过父类释放所有的子类资源,需要在父类的析构函数前加virtual关键字,
//否则会产生内存泄漏。
howtodelete(myC0);
printf("--------------\n");
C *myC1 = new C;
howtodelete2(myC1);
}
#endif
/*
重载、重写、重定义
函数重载:
1、必须在同一个类中进行
2、子类无法重载父类的函数,父类同名函数将被名称覆盖
3、重载是在 编译期间 根据参数类型和个数决定函数调用
函数重写:
1、必须发生于父类与子类之间
2、并且父类与子类中的函数必须有完全相同的函数原型
3、使用virtual声明之后能够产生多态(不使用virtual,那叫重定义)
4、多态是在 运行期间 根据具体对象的类型决定函数调用
*/
#if 0
class Parent
{
public:
void abc()
{
printf("abc\n");
}
virtual void func()
{
cout << "func().." << endl;
}
virtual void func(int i)
{
cout << "func(int i).." << i <<endl;
}
virtual void func(int i, int j)//与子类的函数原型相同
{
cout << "func(int i, int j).." << i <<j<<endl;
}
virtual void func(int i, int j, int m, int n)
{
cout << "func(int i, int j, int m, int n).." << i << j << endl;
}
};
class Child : public Parent
{
public:
void abc()
{
printf("child abc");
}
/*
void abc(int a)
{
printf("child abc");
}
*/
virtual void func(int i, int j)//虚函数的函数原型必须相同
{
cout << "func(int i, int j) do..." << i << " " << j << endl;
}
virtual void func(int i, int j, int k)
{
cout << "func(int i, int j) do.." << endl;
}
protected:
private:
};
void test()
{
Child c1;
/*
error C2661: “Child::func”: 没有重载函数接受 0 个参数
func函数的名字,在子类中发生了名称覆盖;子类的函数的名字,占用了父类
的函数的名字的位置。因为子类中已经有了func名字的重载形式。。。。,
编译器开始在子类中找func函数。。。。但是没有0个参数的func函数
*/
//c1.func();//不符合多态的条件
//子类无法重载父类的函数,父类同名函数将被名称覆盖
c1.Parent::func();
//1 C++编译器 看到func名字 ,因子类中func名字已经存在了(名称覆盖).所以c++编译器不会去找父类的4个参数的func函数
//2 c++编译器只会在子类中,查找func函数,找到了两个func,一个是2个参数的,一个是3个参数的.
//3 C++编译器开始报错..... error C2661: “Child::func”: 没有重载函数接受 4 个参数
//4 若想调用父类的func,只能加上父类的域名..这样去调用..
//c1.func(1, 2, 3, 4);
c1.Parent::func(1, 2, 3, 4);
//子类和父类中都有同名函数abc()且函数原型相同,是重定义
c1.abc();
}
#endif
/*
多态原理探究
多态成立的三个条件:
1、要有继承 2、虚函数重写 3、父类指针指向子类对象
*/
#if 0
class Parent
{
public:
Parent(int a = 0)
{
this->a = a;
}
virtual void print()
{
cout << "我是爹1" << endl;//1 动手脚 写virtal关键字 会特殊处理 //虚函数表
}
virtual void print2()
{
cout << "我是爹2" << endl;//1 动手脚 写virtal关键字 会特殊处理 //虚函数表
}
private:
int a;
};
class Child : public Parent
{
public:
Child(int a = 0, int b = 0) :Parent(a)
{
this->b = b;
}
virtual void print()
{
cout << "我是儿子" << endl;
}
private:
int b;
};
void HowToPlay(Parent *base)
{
base->print();//有多态发生 //2 动手脚
//效果:传来子类对 执行子类的print函数 传来父类对执行父类的print函数
/*
1、C++编译器根本不需要区分是子类对象 还是父类对象
2、父类对象和子类对象分别有vptr指针 , ==>虚函数表===>函数的入口地址
3、迟绑定 (运行时的时候,c++编译器才去判断)
*/
}
void test()
{
Parent p1;//3 动手脚 提前布局 提前有了内存的布局
//用类定义对象的时候 C++编译器会在对象中添加一个vptr指针
Child c1; //子类里面也有一个vptr指针
HowToPlay(&p1);
HowToPlay(&c1);
}
#endif
/*
证明vptr函数指针的存在
*/
#if 0
class Parent1
{
public:
Parent1(int a = 0)
{
this->a = a;
}
void print()
{
cout << "我是爹" << endl;
}
private:
int a;
};
class Parent2
{
public:
Parent2(int a = 0)
{
this->a = a;
}
virtual void print()
{
cout << "我是爹" << endl;
}
private:
int a;
};
void test()
{
printf("sizeof(Parent):%d sizeof(Parent2):%d \n", sizeof(Parent1), sizeof(Parent2));
}
#endif
/*
多态下对象内存布局
*/
#if 0
/*
因为class A带有虚函数,所以A对象的内存布局就要增加一个
虚函数表指针,它是一个指针,指向类A的虚函数表
*/
class A
{
public:
A( int a1 = 0, int a2 = 0)
{
}
virtual void A1() { cout << "A::A1" << endl; }
virtual void A2() { cout << "A::A2" << endl; }
virtual void A3() { cout << "A::A3" << endl; }
protected:
int a1;
int a2;
};
/*
当父类有虚函数时,子类继承父类的虚函数表,而且虚函数的顺序是
先父类的虚函数,再子类的虚函数;当父类的虚函数被子类重写时,
则虚函数表中的父类虚函数指针要 替换 为子类的虚函数指针
*/
class B : public A
{
public:
B(int a1 = 0, int a2 = 0, int b1 = 0):A(a1 = 1,a2 =2)
{
}
virtual void B1() { cout << "B::B1" << endl; }
virtual void A2() { cout << "B::A2" << endl; }//多态
virtual void B2() { cout << "B::B2" << endl; }
protected:
int b1;
};
typedef void(*pfun)();//定义函数指针类型
void test()
{
B *bp= new B;
pfun fun = NULL;//定义一个函数指针变量,用于循环迭代虚函数表
printf("类B的内存模型:\n");
//打印类B的内存模型
for (int i = 0; i < 5; i++)
{
fun = (pfun)*((int *)*(int *)bp + i);//32平台
printf("(int *)bp + %d = %x\n", i,(int *)bp + i);
printf("*((int *)bp + %d) = %x\n", i,*( (int *)bp + i));
printf("fun = %x\n",fun);
fun();
}
cout << *((int*)*(int *)bp + 5) << endl;//输出虚函数表的结束符
}
/*
(long*)bp,
将对象的指针类型转换为(long*)类型,用于取出虚函数表的地址.
*(long*)bp,
*为取出指针指向的值.此式子即虚函数表的地址,也就是第一个虚函数的地址.
(long*)*(long*)bp,
将虚函数表的地址指针转换为(long*),用于后续迭代.
*(long*)*(long*)bp,
*求指针值,即为第一个虚函数的地址,最后转换为pfun指针.
*/
#endif
#if 0
struct A
{
int a;
int b;
int c;
};
void test()
{
printf("sizeof(void *) = %d\n", sizeof(void *));
struct A a = {3,4,5};
struct A *pa;
pa = &a;
printf("%p--- %d\n", (int *)pa, *(int *)pa);
printf("%p--- %d\n", (int *)pa + 1,*((int *)pa+1));
printf("%p--- %d\n", (int *)pa + 2, *((int *)pa + 2));
}
#endif
/*
构造函数中能调用虚函数实现多态么?
对象在创建的时,由编译器对VPTR指针进行初始化
只有当对象的构造完全结束后VPTR的指向才最终确定
父类对象的VPTR指向父类虚函数表
子类对象的VPTR指向子类虚函数表
*/
#if 0
class Parent
{
public:
Parent(int a = 0)
{
this->a = a;
print();//父类中的构造调用不能实现多态
//只有当对象的构造完全结束后VPTR的指向才最终确定
}
virtual void print()
{
cout << "我是爹" << endl;
}
private:
int a;
};
class Child : public Parent
{
public:
Child(int a = 0, int b = 0) :Parent(a)
{
this->b = b;
print();//子类中的构造调用多态不能实现多态
}
virtual void print()
{
cout << "我是儿子" << endl;
}
private:
int b;
};
void test()
{
//定义一个子类对象 ,在这个过程中,在父类构造函数中调用虚函数print 能发生多态吗?
Child c1;
/*
1、要初始化c1.vptr指针,初始化是分步
2、当执行父类的构造函数时,c1.vptr指向父类的虚函数表
当父类的构造函数运行完毕后,会把c1.vptr指向子类的虚函数表
*/
}
#endif
/*
父类指针和子类指针的步长
结论:
多态是用父类指针指向子类对象 和 父类步长++ ,是两个不同的概念
*/
#if 0
class Parent
{
public:
Parent(int a = 0)
{
}
virtual void print()
{
cout << "我是爹" << endl;
}
private:
int a;
};
//成功 ,一次偶然的成功 ,必然的失败更可怕
class Child : public Parent
{
public:
Child(int b = 0) :Parent(0)
{
}
virtual void print()
{
cout << "我是儿子" << endl;
}
private:
//int b;//注释的情况下,父类与子类的 步长一致 不注释掉程序会崩溃
};
void test()
{
Child c1;
Parent *pP = NULL;
Child *pC = NULL;
printf("sizeof(Parent) = %d\n", sizeof(Parent));
printf("sizeof(Child) = %d\n", sizeof(Child));
Child array[] = { Child(1), Child(2), Child(3) };
pP = array;
pC = array;
pP->print();//多态发生 1要有继承 2虚函数重写 3父类指针指向子类对象
pC->print();
pP++;
pC++;
pP->print();//多态发生
pC->print();
pP++;
pC++;
pP->print();//多态发生
pC->print();
}
#endif
/*
纯虚函数抽象类语法基础
1、纯虚函数是一个在基类中说明的虚函数,在基类中没有定义,
要求任何派生类都定义自己的版本
2、纯虚函数为各派生类提供一个公共界面(接口的封装和设计、
软件的功能模块划分)
3、纯虚函数说明形式
virtual 类型 函数名(参数表) = 0;
纯虚函数的作用是在基类中为其派生类保留一个函数的名字,以便派生类根据
需要对它进行重新定义。纯虚函数没有函数体,它最后的“=0”并不表示函数的
返回值为0,只是起形式上的作用,告诉编译系统这是“这是纯虚函数”。
纯虚函数不具备函数的功能,不能被调用。
4、至少含有一个纯虚函数的基类称为抽象类
5、抽象类不能建立对象,可以声明抽象类指针,可以指向它的派生类,进而实现多态性。
6、抽象类不能作为参数类型和函数返回类型或显示转换的类型,可以声明抽象类的引用
7、如果在抽象类中的派生类中没有重新说明纯虚函数,则该函数在派生类中仍然为纯虚函数
,而这个派生类仍然是一个抽象类
*/
#if 0
//面向抽象类编程(面向一套预先定义好的接口编程)
//解耦合 模块的划分
class Figure//抽象类
{
public:
//阅读一个统一的界面(接口),让子类使用,让子类必须去实现
virtual void getArea() = 0;//纯虚函数,基类中就不再给出函数的实现部分
protected:
private:
};
class Circle :public Figure
{
public:
Circle(int a, int b)
{
this->a = a;
this->b = b;
}
//注意实现这个纯虚函数接口的时候,从基类中拷贝最好,保证函数原型正确
virtual void getArea()
{
cout << "圆形的面积:" << 3.14*a*a << endl;
}
private:
int a;
int b;
};
class Tri :public Figure
{
public:
Tri(int a, int b)
{
this->a = a;
this->b = b;
}
virtual void getArea()
{
cout << "三角形的面积: " << a*b / 2 << endl;
}
private:
int a;
int b;
};
class Square : public Figure
{
public:
Square(int a, int b)
{
this->a = a;
this->b = b;
}
virtual void getArea()
{
cout << "四边形的面积: " << a*b << endl;;
}
private:
int a;
int b;
};
void objplay(Figure *base)
{
base->getArea(); //会发生多态
}
void test()
{
//Figure f; //抽象类不能被实例化,因为抽象类中包含一个没有定义功能的纯虚函数
Figure *base = NULL; //可以声明为抽象类的指针
Circle c1(10, 20);
Tri t1(20, 30);
Square s1(50, 60);
c1.getArea();
t1.getArea();
s1.getArea();
//面向抽象类编程(面向一套预先定义好的接口编程)
printf("------------------\n");
objplay(&c1);
objplay(&t1);
objplay(&s1);
}
#endif
/*
多继承的二义性
此种场景下加virtual关键字,则告诉c++编译器让老祖宗的构造函数只执行一次
*/
#if 0
class B
{
public:
int b;
protected:
private:
};
class B1 : virtual public B
{
public:
int b1;
protected:
private:
};
class B2 : virtual public B
{
public:
int b2;
protected:
private:
};
class C : public B1, public B2
{
public:
int c;
protected:
private:
};
void test()
{
C myc;
myc.c = 10;
myc.b = 100;//二义性 error C2385: 对“b”的访问不明确
//加上virtual关键字,让父类只构造一次
}
#endif
/*
抽象类在多继承中的应用
绝大多数面向对象语言都不支持多继承
绝大多数面向对象语言都支持接口的概念
C++中没有接口的概念
C++中可以使用纯虚函数实现接口
接口类中只有函数原型定义,没有任何数据的定义
*/
#if 0
//两个接口类中函数名相同,也不会发生二义性
class Intetface1
{
public:
virtual int add(int a, int b) = 0;//抽象类
virtual void print() = 0;
};
class Intetface2
{
public:
virtual int mult(int a, int b) = 0;//抽象类
virtual void print() = 0;
};
class Parent
{
public:
int getA()
{
a = 0;
return a;
}
protected:
private:
int a;
};
class Child :public Parent, public Intetface1, public Intetface2
{
public:
virtual int add(int a, int b)
{
cout << "Child: add()已经执行\n";
return a + b;
}
virtual int mult(int a, int b)
{
cout << "Child: mult()已经执行\n";
return a*b;
}
virtual void print()
{
cout << "Child: print()已经执行\n";
}
protected:
private:
};
void test()
{
Child c1;
c1.print();//没有使用多态
//声明指向抽象类的指针变量,指针指向它的派生类,实现多态
Intetface1 *it1 = &c1;
it1->add(1, 2); //多态 1、要有继承 2、要有虚函数重写 3、用父类指针(父类引用)指向子类对象
Intetface2 *it2 = &c1;
it2->mult(1, 2); //多态
}
#endif
/*
面向抽象编程类计算程序员工资
*/
#if 0
/*
编写一个C++程序, 计算程序员( programmer )工资
1 要求能计算出初级程序员( junior_programmer ) 中级程序员 ( mid_programmer )高级程序员( adv_programmer)的工资
2 要求利用抽象类统一界面,方便程序的扩展, 比如:新增, 计算 架构师 (architect ) 的工资
*/
class programmer
{
public:
virtual void getSal() = 0;//纯虚函数 界面统一接口
};
class junior_programmer : public programmer
{
public:
junior_programmer(char *name, char *job, int sal)
{
this->name = name;
this->job = job;
this->sal = sal;
}
virtual void getSal()
{
cout << name << " " << job << " : " << sal << endl;
}
private:
char * name;
char *job;
int sal;
};
class mid_programmer : public programmer
{
public:
mid_programmer(char *name, char *job, int sal)
{
this->name = name;
this->job = job;
this->sal = sal;
}
virtual void getSal()
{
cout << name << " " << job << " : " << sal << endl;
}
private:
char *name;
char *job;
int sal;
};
//adv_programmer
class adv_programmer : public programmer
{
public:
adv_programmer(char *name, char *job, int sal)
{
this->name = name;
this->job = job;
this->sal = sal;
}
virtual void getSal()
{
cout << name << " " << job << " : " << sal << endl;
}
private:
char *name;
char *job;
int sal;
};
class architect : public programmer
{
public:
architect(char *name, char *job, int sal)
{
this->name = name;
this->job = job;
this->sal = sal;
}
virtual void getSal()
{
cout << name << " " << job << " : " << sal << endl;
}
private:
char *name;
char *job;
int sal;
};
void CalProgSal(programmer *base)
{
base->getSal();//将发生多态
}
void test()
{
junior_programmer jp("小张", "初级程序员", 4000);
mid_programmer mp("小李","中级程序员", 7000);
adv_programmer ap("小王", "高级程序员", 15000);
//系统扩展
architect ar("传智高水平学员", "架构师", 24000);
CalProgSal(&jp);
CalProgSal(&mp);
CalProgSal(&ap);
CalProgSal(&ar);
}
#endif
#if 0
//数组指针 语法 梳理
//定义一个数组类型
//int a[10];//
//定义一个指针数组类型
//定义一个指向 数组类型的指针 数组类的指针
void test()
{
//a代表数组首元素的地址 &a代表整个数组的地址
//a+1 4 &a +1 40
int a[10];
//定义一个数组类型
{
typedef int(myTypeArray)[10];
myTypeArray myArray;
myArray[0] = 10;
printf("%d \n", myArray[0]);
}
//定义一个数组指针类型
{
typedef int(*PTypeArray)[10];//int *p
PTypeArray myPArray;
myPArray = &a;
(*myPArray)[0] = 20;
printf("a[0] = %d\n", a[0]);
}
//定义一个指向 数组类型的指针 数组类的指针
{
int(*MyPointer)[10];//变量 告诉c编译器给我分配内存
MyPointer = &a;
(*MyPointer)[0] = 40;
printf("a[0] = %d\n", a[0]);
}
}
#endif
//函数指针语法梳理
//1 如何定义一个函数类型
//2 如何定义一个函数指针类型
//3 如何定义一个 函数指针 (指向一个函数的入口地址)
#if 0
int add(int a, int b)
{
printf("func add .....\n");
return a + b;
}
void test()
{
add(1, 2);//直接调用 函数名是函数的入口地址
//1 如何定义一个函数类型
{
typedef int MyFuncType(int a, int b);
//typedef int (MyFuncType)(int a, int b);
MyFuncType *myPointerFunc = NULL;
myPointerFunc = &add;
myPointerFunc(3, 4);//间接调用
myPointerFunc = add;//细节 c逐渐过程 兼容历史版本的原因
myPointerFunc(3, 4);
}
//2 如何定义一个函数指针类型
{
typedef int(*MyPointerFuncType)(int a, int b);//int *a = NULL
MyPointerFuncType myPointerFunc;//定义一个指针
myPointerFunc = add;
myPointerFunc(5, 6);
}
//3 如何定义一个 函数指针 (指向一个函数的入口地址)
{
int(*MyPointerFunc)(int a, int b);//定义了一个变量
MyPointerFunc = add;
MyPointerFunc(7, 8);
}
}
#endif
/*
函数指针做函数参数的思想剖析
*/
#if 0
int myadd(int a, int b) //子任务的实现者
{
printf("func add() do...\n");
return a + b;
}
int myadd2(int a, int b) //子任务的实现者
{
printf("func add2() do...\n");
return a + b;
}
int myadd3(int a, int b) //子任务的实现者
{
printf("func add3() do...\n");
return a + b;
}
int myadd4(int a, int b) //子任务的实现者
{
printf("func add4() do...\n");
return a + b;
}
//定义一个类型 相当于c++中的多态提前布局 函数指针类型就是规定好了协议(返回值、参数)
typedef int(*MyTypeFuncAdd)(int a, int b);
//函数指针做函数参数
int MainOp(MyTypeFuncAdd myFuncAdd)
{
int c = myFuncAdd(5, 6);
return c;
}
//定义一个函数类型变量
//int(*MyTypeFuncAdd)(int a, int b);
int MainOp2(int(*MyTypeFuncAdd)(int a, int b))
{
int c = MyTypeFuncAdd(5, 6);
return c;
}
//间接调用
//任务的调用 和 任务的编写可以分开
void test()
{
MyTypeFuncAdd myFuncAdd = NULL;
myadd(1, 2);//直接调用
myFuncAdd = myadd;
myFuncAdd(3, 4);//间接调用
MainOp(myadd);
MainOp2(myadd);
//在mainop框架 没有发生任何变化的情况下 ...
MainOp(myadd2);
MainOp(myadd3);
MainOp(myadd4);
}
#endif
/*
函数模板定义及2种调用方法
法一: 显示类型 调用
法二: 自动类型 推导
*/
#if 0
//函数的业务逻辑一样,只是函数的参数类型不同
void myswap01(int &a, int &b)
{
int c = 0;
c = a;
a = b;
b = c;
}
void myswap02(char &a, char &b)
{
char c = 0;
c = a;
a = b;
b = c;
}
/*
让类型参数化,方便程序员编码 称为 泛型编程
template 告诉c++编译器 我要开始泛型编程了 看到T,不要随便报错
template语句与函数模板定义语句之间不能有别的语句
*/
template <typename T>
void myswap(T &a, T &b)
{
T c = 0;
c = a;
a = b;
b = c;
cout << "hello ...我是模板函数, 欢迎call me!!" << endl;
}
void test()
{
int x = 1;
int y = 2;
char a = 'g';
char b = 'h';
//法1 函数模板 显示类型调用
myswap<int>(x, y);
printf("x = %d y = %d\n", x, y);
myswap<char>(a, b);
printf("a = %c b = %c\n", a, b);
//法2 自动类型推导 不常用
myswap(x, y);
}
#endif
/*
函数模板当函数参数
函数模板的本质:类型参数化
*/
#if 0
//对字符数组和整形数组进行排序
template <typename T1, typename T2>
int mysort(T1 *array, T2 size)
{
T2 i, j;
T1 tmp;
if (array == NULL)
{
return -1;
}
for (i = 0; i < size; i++)
{
for ( j = i+1; j < size; j++)
{
if (array[i] < array[j])
{
tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
}
}
return 0;
}
template<typename T1, typename T2>
void myprint(T1 * array, T2 size)
{
T2 i = 0;
for ( i = 0; i < size; i++)
{
cout << array[i] << " ";
}
}
void test()
{
//int类型
int array[] = { 3,11,33,44,55,9 };
int size = sizeof(array) / sizeof(int);
mysort<int, int>(array, size);
myprint<int, int>(array, size);
printf("--------------------\n");
//char 类型
char buf[] = "awegfgddhh";
int len = strlen(buf);
mysort<char, int>(buf, len);
myprint<char, int>(buf, len);
}
#endif
/*
函数模板遇上函数重载
*/
#if 0
template <typename T>
void myswap(T &a, T &b)
{
T c = 0;
c = a;
a = b;
b = c;
cout << "hello ...." << endl;
}
void myswap(int a, char c)
{
cout << "a:" << a << "c:" << c << endl;
cout << "我是普通函数" << endl;
}
void test()
{
int a = 10;
char c = 'z';
myswap(a, c);
//普通函数的调用:可以进行隐式类型转换
myswap(c, a);
//函数模板的调用(本质:类型参数化):将严格按照类型匹配,不会进行自动类型转换
myswap(a, a);
}
#endif
/*
函数模板不允许自动类型转化
普通函数能够进行自动类型转换
4条规则:
1、函数模板可以像普通函数一样被重载
2、c++编译器优先考虑普通函数
3、如果函数模板可以产生一个更好的匹配,那么选择模板
4、可以通过空模板实参列表的语法限定编译器只通过模板匹配
*/
#if 0
int Max(int a, int b)
{
cout << "int Max(int a, int b)" << endl;
return a > b ? a : b;
}
template<typename T>
int Max(T a, T b)
{
cout << "int Max<>(T a, T b)" << endl;
return a > b ? a : b;
}
template<typename T>
int Max(T a, T b, T c)
{
cout << "int Max(T a, T b, T c)" << endl;
return Max(Max(a, b), c);
}
void test()
{
int a = 1;
int b = 2;
//当函数模板和普通函数都符合调用时,优先选择普通函数
cout << Max(a, b) << endl;
//若显示使用函数模板,则使用<>类型列表
cout << Max<>(a, b) << endl;
//如果函数模板产生更好的匹配,使用函数模板
cout << Max(3.0, 4.0) << endl;
//重载
cout << Max(5.0, 6.0, 7.0) << endl;
//调用普通函数 可以隐式 类型转换
cout << Max('a', 100) << endl;
}
#endif
/*
函数模板机制结论:
编译器并不是把函数模板处理成能够任意类的函数
编译器从函数模板通过具体类型产生不同的函数
编译器会对模板进行2次编译:
(1)在声明的地方对模板代码本身进行编译
(2)在调用的地方对参数替换后的代码进行编译
*/
#if 0
// g++ -S 1.cpp -o 1.s
template <typename T>
void myswap(T &a, T &b)
{
T c = 0;
c = a;
a = b;
b = c;
cout << "hello ....我是模板函数 欢迎 calll 我" << endl;
}
void test()
{
{
int x = 10;
int y = 20;
myswap<int>(x, y); //1 函数模板 显示类型 调用
printf("x:%d y:%d \n", x, y);
}
{
char a = 'a';
char b = 'b';
myswap<char>(a, b); //1 函数模板 显示类型 调用
printf("a:%c b:%c \n", a, b);
}
}
#endif
/*
单个类 模板基本语法
模板类 类型参数化
类模板的定义、使用、类模板作为函数参数
*/
#if 0
template <typename T>
class A
{
public:
A(T a = 0)
{
this->a = a;
}
void printA()
{
cout << "a: " << a << endl;
}
protected:
T a;
};
//类模板做函数参数
//参数 ,c++编译器 要求具体的类 , 所以要A<int>&a
void UseA(A<int> &a)
{
a.printA();
}
/*
类模板定义对象时,如下形式
类模板名<实际类型名>对象名(实参表列)
*/
void test()
{
//模板类(本身就是类型化的)==》具体的类==》定义具体的变量
A <int>a1(11), a2(12), a3(30);//模板类是抽象的,需要进行类型具体
a1.printA();
UseA(a1);
UseA(a2);
UseA(a3);
}
#endif
/*
从模板类派生普通类
*/
#if 0
template <typename T>
class A
{
public:
A(T a)//构造函数写了,就必须用
{
this->a = a;
}
void printA()
{
cout << "a: " << a << endl;
}
protected:
T a;
};
/*
从模板类派生普通类
模板类派生时,需要具体化模板类,c++编译器需要知道 父类的数据类型具体是什么样子的
==》要知道父类所占的内存大小是多少 只有数据类型固定下来,才知道如何分配内存
*/
class B : public A<int>
{
public:
B(int a = 10, int b = 20) : A<int>(a)
{
this->b = b;
}
void printB()
{
cout << "a: " << a << "b: " << b << endl;
}
protected:
private:
int b;
};
void test()
{
B b1(1, 2);
b1.printB();
}
#endif
/*
从模板类派生模板类
*/
#if 0
template <typename T>
class A
{
public:
A(T a)//构造函数写了,就必须用
{
this->a = a;
}
void printA()
{
cout << "a: " << a << endl;
}
protected:
T a;
};
template <typename T>
class C : public A<T>
{
public:
C(T c, T a) : A<T>(a)
{
this->c = c;
}
void printC()
{
cout << "c:" << c << endl;
}
protected:
T c;
};
void test()
{
C<int> c1(1, 2);
c1.printC();
}
#endif
/*
复数类所有函数都写在函数的内部
*/
#if 0
template <typename T>
class Complex
{
public:
Complex(T a, T b)
{
this->a = a;
this->b = b;
}
friend Complex MySub(Complex &c1, Complex &c2)
{
Complex tmp(c1.a - c2.a, c1.b - c2.b);
return tmp;
}
friend ostream & operator<<(ostream &out, Complex &obj)
{
out << obj.a <<" + "<<obj.b<< "i"<< endl;
return out;
}
Complex operator+(Complex &obj)
{
Complex tmp(this->a + obj.a, this->b + obj.b);
return tmp;
}
void printCom()
{
cout << "a: " << a << " b: " << b << endl;
}
private:
T a;
T b;
};
//运算符重载的正规写法
//重载<< >> 只能用友元函数,其他运算符重载都要写成成员函数,不要滥用友元函数
/*
ostream & operator<<(ostream &out, Complex &obj)
{
out << obj.a << " + " << obj.b << "i" << endl;
return out;
}
*/
void test()
{
//需要把模板类进行具体化以后 才能定义对象 c++编译器要分配内存
Complex<int> c1(1, 2);
c1.printCom();
Complex<int> c2(3, 4);
Complex<int> c3 = c1 + c2;
c3.printCom();
cout << c3 << endl;
//滥用友元函数
{
Complex<int> c4 = MySub(c1, c2);
cout << c4 << endl;
}
}
#endif
/*
复数类所有函数都写在函数的外部
*/
#if 0
//-------------------------------
//滥用友元函数的添加
template <typename T>
class Complex; //类的前置声明
template <typename T>
Complex<T> MySub(Complex<T> &c1, Complex<T> &c2);
//--------------------------------------------------------
template <typename T>
class Complex
{
friend Complex<T> MySub<T>(Complex<T> &c1, Complex<T> &c2);
friend ostream & operator<< <T> (ostream &out, Complex &obj);
//friend ostream & operator<<(ostream &out, Complex &obj);
public:
Complex(T a, T b);
void printCom();
Complex operator+(Complex &obj);
private:
T a;
T b;
};
/*
成员函数从类的内部移到类的外部:
1、在成员函数之前进行模板声明 template<typename T>
2、在成员函数名前缀上 "类名<类型参数>::" Complex<T>::
在类模板外定义成员函数的一般形式如下
template <typename 类型参数>
函数类型 类名<类型参数>::成员函数名(形参表)
注意友元函数是全局函数,不是成员函数
*/
template<typename T>
void Complex<T>::printCom()
{
cout << "a: " << a << " b: " << b << endl;
}
template<typename T>
Complex<T>::Complex(T a, T b)
{
this->a = a;
this->b = b;
}
template<typename T>
Complex<T> Complex<T>::operator+(Complex<T> &obj)
{
Complex tmp(this->a + obj.a, this->b + obj.b);
return tmp;
}
/*
注意友元函数是全局函数,是不属于这个类的
出现以下Bug的原因:
本质是:模板
错误 LNK2019 无法解析的外部符号
"class std::basic_ostream<char,struct std::char_traits<char> > &
__cdecl operator<<
(class std::basic_ostream<char,struct std::char_traits<char> > &,
class Complex<int> &)"
(??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@AAV?$Complex@H@@@Z),
该符号在函数 "void __cdecl test(void)" (?test@@YAXXZ) 中被引用 生命周期管理
G:\DAY\cproj\c++Proj\生命周期管理\生命周期管理\dm01类的构造和析构基础.obj 1
*/
template<typename T>
ostream & operator<<(ostream &out, Complex<T> &obj)
{
out << obj.a << " + " << obj.b << "i" << endl;
return out;
}
//滥用友元函数
template<typename T>
Complex<T> MySub(Complex<T> &c1, Complex<T> &c2)
{
Complex<T> tmp(c1.a - c2.a, c1.b - c2.b);
return tmp;
}
void test()
{
//需要把模板类进行具体化以后 才能定义对象 c++编译器要分配内存
Complex<int> c1(1, 2);
c1.printCom();
Complex<int> c2(3, 4);
Complex<int> c3 = c1 + c2;
c3.printCom();
cout << c3 << endl;
//滥用友元函数
{
Complex<int> c4 = MySub(c1, c2);
cout << c4 << endl;
}
}
#endif
/*goto 语句在多层嵌套结构中的应用*/
#if 0
void test()
{
int i = 0;
while (1)
{
i++;
if (i == 5)
goto END;
}
END:
printf(" END i = %d\n", i);
}
#endif
/*
****类模板中的static关键字****
编译器并不是把函数模板处理成能够处理任意类的函数
编译器从函数模板通过具体类型产生不同的函数,编译器会
对函数模板进行两次编译
在声明的地方对模板代码本身进行编译;
在调用的地方对参数替换后的代码进行编译
*/
#if 0
template <typename T>
class AA
{
public:
static T m_a;
protected:
private:
};
template <typename T>
T AA<T>::m_a = 0;
void test()
{
AA<int> a1, a2, a3;
a1.m_a++;
a2.m_a++;
a3.m_a++;
cout << AA<int>::m_a << endl;
AA<char> b1, b2, b3;
b1.m_a = 'a';
b2.m_a++;
b3.m_a++;
//m_a 应该是 每一种类型的类 使用自己的m_a
cout << AA<char>::m_a << endl;
}
#endif
/*
C++类型转换
C风格的强制类型转换(Type Cast)很简单,不管什么类型的转换统统是:
TYPE b = (TYPE)a
C++风格的类型转换提供了4种类型转换操作符来应对不同场合的应用。
static_cast 静态类型转换。如int转换成char
reinterpreter_cast 重新解释类型
dynamic_cast 命名上理解是动态类型转换。如子类和父类之间的多态类型转换。
const_cast, 字面上理解就是去除变量的const属性。
4种类型转换的格式:
TYPE B = static_cast<TYPE> (a)
*/
#if 0
void test()
{
double dpi = 3.1415926;
//c类型转换
int num1 = (int)dpi;
//静态类型转换 编译时c++编译器会做类型检查
int nmu2 = static_cast<int>(dpi);
//c语言中 隐式类型转换的地方 均可使用 static_cast<>() 进行类型转换
int num3 = dpi;
//char * ===> int *
char *p1 = "hello world";
int *p2 = NULL;
//使用static_cast, 编译器编译时,会做类型检查 若有错误 提示错误
//p2 = static_cast<int*>(p1);
p2 = reinterpret_cast<int*>(p1);
cout << "p1" << p1 << endl; //相当于 %s
cout << "p2" << p2 << endl; //相当于 %d
//总结:通过 reinterpret_cast<>() 和 static_cast<>()把C语言的强制类型转换 都覆盖了
}
#endif
/*
dynamic_cast命名上理解是动态类型转换。如子类和父类之间的多态类型转换。
*/
#if 0
class Tree
{
};
//包含纯虚函数的类称之为抽象类
class Animal
{
public:
virtual void cry() = 0;//纯虚函数
};
class Dog :public Animal
{
public:
virtual void cry()
{
cout << "汪汪" << endl;
}
void dohome()
{
cout << "看家" << endl;
}
};
class Cat :public Animal
{
public:
virtual void cry()
{
cout << "喵喵" << endl;
}
void dothing()
{
cout << "抓老鼠" << endl;
}
};
void playobj(Animal *base)
{
base->cry();//1有继承 2 虚函数重写 3父类指针指向子类对象 ==>多态
}
void playobj2(Animal *base)
{
//能识别子类对象 运行时类型识别
Dog *pDog = dynamic_cast<Dog *>(base);//转换不成功则指针为NULL
if (pDog != NULL)
{
pDog->dohome();//让 能够做自己的工作
}
Cat *pCat = dynamic_cast<Cat *>(base);//父类对象 ==>子类对象 向下转型 把老子转化为小子
if (pCat != NULL)
{
pCat->dothing();//让 能够做自己的工作
}
}
void test()
{
Dog d1;
Cat c1;
//Animal pBase; 不能定义抽象类对象 只能定义抽象类指针
Animal *pBase = NULL;
//子类对象复制给父类
pBase = &d1;//第一种写法
//c++编译器在编译的时候会进行类型检查
pBase = static_cast<Animal *>(&d1);//第二种写法
//强制类型转换
pBase = reinterpret_cast<Animal *>(&d1);//第三种写法
{
Tree t1;
//pBase = static_cast<Animal *>(&t1);
//强制类型转换 Tree 类型强制转化为 Animal
pBase = reinterpret_cast<Animal *>(&t1);
}
playobj(&d1);
playobj(&c1);
playobj2(&d1);
playobj2(&c1);
}
#endif
/*
const_cast去除变量的const属性。
*/
#if 0
void printBuf(const char * p)
{
//p[0] = 'k';
char *p1 = NULL;
//程序员 要清楚的知道 变量 : 转换之前是什么类型, 转换之后是什么类型
//const char * ===> char * //把只读属性 去掉
p1 = const_cast<char *>(p);
p1[0] = 'K';//通过p1 去修改了内存空间
cout << p << endl;
}
void test()
{
char buf[] = "123456";//分配了内存
char *myp = "hhhhhh";//在静态存储区
printBuf(buf);
//程序员 要确保 p所指向的内存空间 确实能修改 ;如果不能修改会带来灾难性后果
printBuf(myp);
}
#endif
/*
异常的基本语法
1 基本语法
2 发生异常之后,是跨函数 :
3 接受异常以后 可以不处理 再抛出异常
4 catch异常的时 按照类型进行catch
5 异常捕捉严格按照类型匹配
*/
#if 0
void devide(int x, int y)
{
if (y == 0)
{
throw x;//抛出int类型异常
}
cout << "devide 结果" << x / y << endl;
}
void myDevide(int x, int y)
{
try
{
devide(x, y);
}
catch (...)
{
cout << "我接受了 divide的异常 但是我没有处理 我向上抛出" << endl;
throw;
}
}
void test()
{
try
{
devide(10, 2);
//devide(100, 0);
myDevide(344, 0);
}
catch (int e)
{
cout << e << "被零除" << endl;
}
catch (...)//... 代表其他未知类型
{
cout << "其他未知错误" << endl;
}
}
//异常捕捉严格按照类型匹配
void test01()
{
try
{
throw 'Z';
}
catch (int e)
{
cout << "捕获int类型异常" << endl;
}
catch (...)
{
cout << " 未知 类型异常" << endl;
}
}
#endif
/*
异常被抛出后,从进入try块起,到异常被抛掷前,这期间在栈上的构造的所有对象,
都会被自动析构。析构的顺序与构造的顺序相反。这一过程称为栈的解旋
为了加强程序的可读性,可以在函数声明中列出可能抛出的所有异常类型
void func() throw (A, B, C, D);
这个函数func()能够且只能抛出类型A B C D及其子类型的异常
*/
#if 0
class Test3
{
public:
Test3(int a, int b)
{
this->a = a;
this->b = b;
cout << "构造函数do" << endl;
}
~Test3()
{
cout << "析构函数do" << endl;
}
private:
int a;
int b;
};
//不写,可以抛掷任何类型的异常
void myDivide01()
{
Test3 t1(1, 2), t2(3, 4);
cout << "myDevide 要发生异常" << endl;
throw 1;//相当于return
}
//抛出的类型只能是所列出的类型
void myDivide02() throw(int, char, char *)
{
Test3 t1(1, 2), t2(3, 4);
cout << "myDevide 要发生异常" << endl;
throw t1;//测试
throw 1;//相当于return
}
//不抛掷任何类型异常
void myDivide03() throw()
{
Test3 t1(1, 2), t2(3, 4);
cout << "myDevide 要发生异常" << endl;
throw 1;//相当于return
}
void test()
{
try
{
//myDivide01();
myDivide02();
//myDivide03();
}
catch (int a)
{
cout << "int 类型异常"<<endl;
}
catch (...)
{
cout << "未知异常" << endl;
}
}
#endif
/*
传统错误处理
结论:
C++编译器通过throw 来产生对象,C++编译器再执行对应的catch分支,
相当于一个函数调用,把实参传递给形参。
*/
#if 0
//throw int 类型异常
void my_strcpy1(char *to, char * from)
{
if (from == NULL)
{
throw 1;//抛出整型对象
}
if (to == NULL)
{
throw 2;
}
if (*from == 'a')
{
throw 3;
}
while (*from != '\0')
{
*to = *from;
to++;
from++;
}
*to = '\0';
}
//throw char* 类型异常
void my_strcpy2(char *to, char * from)
{
if (from == NULL)
{
throw "源buf出错";//抛出字符串,存储在静态存储区
}
if (to == NULL)
{
throw "目的buf出错";
}
if (*from == 'a')
{
throw "copy过程出错"; //copy时出错
}
while (*from != '\0')
{
*to = *from;
to++;
from++;
}
*to = '\0';
}
class BadSrcType {};
class BadDestType {};
class BadProcessType
{
public:
BadProcessType()
{
cout << "BadProcessType 构造函数do \n";
}
BadProcessType(const BadProcessType &obj)
{
cout << "BadProcessType copy构造函数do \n";
}
~BadProcessType()
{
cout << "BadProcessType 析构函数do \n";
}
};
//throw 类对象 类型异常
void my_strcpy3(char *to, char * from)
{
if (from == NULL)
{
throw BadSrcType();//注意 调用无参不加括号,这里加了括号,记住就可以了
}
if (to == NULL)
{
throw BadDestType();//抛出一个子类对象
}
if (*from == 'a')
{
printf("开始 BadProcessType类型异常 \n");
throw BadProcessType(); //会产生一个对象(不管是匿名还有有名)
}
if (*from == 'b')
{
printf("开始 BadProcessType类型异常 \n");
throw &(BadProcessType()); //会产生一个对象(指针)(不管是匿名还有有名)
}
if (*from == 'c')
{
printf("开始 BadProcessType类型异常 \n");
throw new BadProcessType; //抛出一个对象指针,分配了内存
}
while (*from != '\0')
{
*to = *from;
to++;
from++;
}
*to = '\0';
}
void test()
{
int ret = 0;
char buf1[] = "abcde";
//char buf1[] = "bcde";
//char buf1[] = "cde";
char buf2[1024] = { 0 };
try
{
//my_strcpy1(NULL, buf1);
//my_strcpy2(NULL, buf1);
my_strcpy3(buf2, buf1);
}
catch (int e)//e 可以写 也可以不写
{
cout << e << " int 类型异常" << endl;
}
catch (char *e)//e 可以写 也可以不写 指针是谁分配的内存
{
cout << e << " char 类型异常" << endl;
}
//---
catch (BadSrcType e)
{
cout << " BadSrcType 类型异常" << endl;
}
catch (BadDestType e)
{
cout << " BadDestType 类型异常" << endl;
}
#if 0
//结论1 如果 接受异常的时候,使用一个异常变量,则copy构造异常变量
catch (BadProcessType e) //把匿名对象copy给e
{
cout << " BadProcessType 类型异常" << endl;
}
#endif
#if 1
//选择引用比较合适
//结论2 使用引用的话 会使用throw时候的那个对象
//catch 一个元素和引用不能同时存在
catch (BadProcessType &e) //把匿名对象copy给e
{
cout << " BadProcessType 类型异常" << endl;
}
#endif
//结论3: 指针可以和 引用/元素写在一块 但是引用/元素不能写在一块
catch (BadProcessType *e)
{
cout << " BadProcessType 类型异常" << endl;
delete e;//使用new时候,需要delete
}
//结论4: 类对象时, 使用引用比较合适
// --
catch (...)
{
cout << "未知 类型异常" << endl;
}
}
#endif
/*
异常的层次结构 (继承在异常中的应用)
*/
#if 1
class MyArray
{
public:
MyArray(int len);
~MyArray();
public:
int& operator[](int index);
int getLen();
#if 0
class eNegative{};
class eZero{};
class eTooBig{};
class eTooSmall{};
class eSize{};
#else
//内部类
//基类
class eSize
{
public:
eSize(int size)
{
m_size = size;
}
//虚函数
virtual void printErr()
{
cout << "size:" << m_size << " ";
}
protected:
int m_size;
};
//让类发生继承
class eNegative :public eSize
{
public:
eNegative(int size) : eSize(size)//父类构造函数写了就必须用
{
;
}
virtual void printErr()
{
cout << "eNegative 类型 size:" << m_size << " ";
}
};
class eZero :public eSize
{
public:
eZero(int size) :eSize(size)
{
;
}
virtual void printErr()
{
cout << "eZero 类型 size:" << m_size << " ";
}
};
class eTooBig :public eSize
{
public:
eTooBig(int size) :eSize(size)
{
;
}
virtual void printErr()
{
cout << "eTooBig 类型 size:" << m_size << " ";
}
};
class eTooSmall :public eSize
{
public:
eTooSmall(int size) :eSize(size)
{
;
}
virtual void printErr()
{
cout << "eTooSmall 类型 size:" << m_size << " ";
}
};
#endif
private:
int *m_space;
int m_len;
};
MyArray::MyArray(int len)
{
if (len < 0)
{
throw eNegative(len);//抛出的子类对象
}
else if (len == 0)
{
throw eZero(len);
}
else if (len > 1000)
{
throw eTooBig(len);
}
else if (len < 3)
{
throw eTooSmall(len);
}
m_len = len;
m_space = new int[len];
}
MyArray::~MyArray()
{
if (m_space != NULL)
{
delete[]m_space;
m_space = NULL;
m_len = 0;
}
}
int& MyArray::operator[](int index)
{
return m_space[index];
}
int MyArray::getLen()
{
return m_len;
}
//不推荐
void test01()
{
try
{
MyArray a(-5);
for (int i = 0; i < a.getLen(); i++)
{
a[i] = i + 1;
printf("%d", a[i]);
}
}
catch (MyArray::eNegative e)
{
cout << "eNegative 类型异常" << endl;
}
catch (MyArray::eZero e)
{
cout << "eZero 类型异常" << endl;
}
catch (MyArray::eTooBig e)
{
cout << "eTooBig 类型异常" << endl;
}
catch (MyArray::eTooSmall e)
{
cout << "eTooSmall 类型异常" << endl;
}
catch (...)
{
}
}
void test()
{
try
{
MyArray a(-5);
for (int i = 0; i < a.getLen(); i++)
{
a[i] = i + 1;
printf("%d", a[i]);
}
}
catch (MyArray::eSize &e) //原则:throw的子类 catch父类
{
e.printErr();//发生多态
}
catch (...)
{
}
}
#endif
#if 0
class Teacher
{
public:
Teacher(int age)
{
if (age > 100)
{
string s = "年龄太大";
//out_of_range 表示一个参数不在允许的范围之内
throw out_of_range(s);
}
}
private:
int age;
};
void test()
{
try
{
Teacher t1(102);
}
catch (out_of_range e)
{
cout << e.what() << endl;
}
//exception e;
}
#endif
/*
标准异常基类exception,它的成员函数what()
函数原型:
virtual const char * what() const;//最右边的const修饰的是this指针
*/
#if 0
class MyException :public exception
{
public:
MyException(const char *p)
{
this->m_p = p;
}
virtual const char * what()const
{
cout << "MyException: 类型" << m_p << endl;
return m_p;
}
protected:
private:
const char *m_p;
};
void testMyExcept()
{
throw MyException("函数异常");//抛出子类对象
}
void test()
{
try
{
testMyExcept();
}
catch (MyException &e)//使用引用
{
e.what();//没有发生多态
}
//catch (exception &e)//使用引用
//{
// e.what();//发生了多态
//}
}
#endif
/*
I/O流的概念和流类库
标准I/O:
对系统指定的标准设备的输入和输出。从键盘输入数据,输出到显示器屏幕
文件I/O:
以外存磁盘文件为对象进行输入和输出,即从磁盘文件输入数据,数据输出到磁盘文件。
串I/O:
对内存中指定的空间进行输入和输出。通常指定一个字符数组作为存储空间(实际上
可以利用该空间存储任何信息)
C++的I/O操作是可扩展的,输出标准数据也可以输出自定义数据
iostream:输入输出流
>>a表示将数据放入a对象中。
<<a表示将a对象中存储的数据拿出
标准输入流对象cin
cin.get() //一次只能读取一个字符
cin.get(一个参数) //读一个字符
cin.get(三个参数) //可以读字符串
cin.getline()可以接受空格
cin.ignore() 忽略缓冲区的个数
cin.peek() 偷窥,打开门若缓冲区有数据就取缓冲区的第一个字符,没有就拉倒
cin.putback() 将读取缓冲区中的第一个字符再还给缓冲区
*/
#if 0
void test01()
{
char mybuf[1024];//注意分配内存
int myInt;
long myLong;
cin >> myInt;
cin >> myLong;
cin >> mybuf;//遇见空格停止接受数据
cout << "myInt = " << mybuf << endl << "myLong = " << myLong << endl << "mybuf = " << mybuf << endl;
}
//cin.get() //一次只能读取一个字符
void test02()
{
//EOF 用ctrl+Z 再按下enter
char ch;
while ((ch = cin.get()) != EOF)
{
cout << ch << endl;
}
}
//cin.get(一个参数) //读一个字符
void test03()
{
char c1, c2, c3;
cout << "cin.get()如果缓冲区没有数据,则程序阻塞" << endl;
cin.get(c1);
cin.get(c2);
cin.get(c3);
cout << "---------" << endl;
cout << c1 << c2 << c3 << "因为缓冲区有数据,程序不会阻塞\n";
cin.get(c1).get(c2).get(c3);//支持链式编程
cout << "--" << c1 << c2 << c3 << endl;
}
//cin.getline()可以接受空格
void test04()
{
char buf1[256];
char buf2[256];
cout << "请输入一个字符串,含有多个空格aa bb cc dd \n";
cin >> buf1;//遇到空格就会结束输入
cin.getline(buf2, 256);
cout << "buf1:" << buf1 << "buf2" << buf2 << endl;
}
//cin.ignore() 忽略缓冲区的个数
void test05()
{
char buf1[256];
char buf2[256];
cout << "请输入一个字符串,含有多个空格aa bbccdd \n";
cin >> buf1;//遇到空格就会结束输入
cin.ignore(2);//忽略2 个字符 ,若忽略的字符个数大于缓冲区的字符个数,会阻塞的
//cin.ignore(20);
int myint = cin.peek();//偷窥下缓冲区是否有数据,若有,则读取缓冲区的第一个数据
cout << "myint" << myint << endl;//一般用于网络上
cin.getline(buf2, 256);
cout << "buf1:" << buf1 << "\nbuf2:" << buf2 << endl;
}
//案例:输入的整数和字符串分开处理
void test06()
{
cout << "Please, enter a number or a word: ";
char c = std::cin.get();//先从缓冲区读取了一个字符
if ((c >= '0') && (c <= '9')) //输入的整数和字符串 分开处理
{
int n; //整数不可能 中间有空格 使用cin >>n
cin.putback(c);
cin >> n;
cout << "You entered a number: " << n << '\n';
}
else
{
string str;
cin.putback(c);//将字符在吐回给缓冲区
//cin.getline(str);
getline(cin, str); // //字符串 中间可能有空格 使用 cin.getline();
cout << "You entered a word: " << str << '\n';
}
}
void test()
{
//test01();
//test02();
//test03();
//test04();
//test05();
test06();
}
#endif
/*
c++格式化输出
标准输出流对象cout
cout.flush() 刷新缓冲区
cout.put()
cout.write()//进行二进制流的输入和输出
cout.width()
cout.fill()
cout.setf(标记)
manipulator(操作符、控制符)
flush
endl
oct
dec
hex
setbase
setw
setfill
setprecision
…
*/
#if 0
#include <iomanip>
void test01()
{
cout << "hello" << endl;
cout.put('h').put('e').put('l') << endl;
char *p = "12345 itcast";
cout.write(p, strlen(p)) << endl;
cout.write(p, strlen(p) - 4) << endl;
cout.write(p, strlen(p) + 4) << endl;
}
void test02()
{
//使用流对象的有关类成员函数
cout << "<start>";
cout.width(30);
cout.fill('*');
cout.setf(ios::showbase);
//cout.setf(ios::internal);//注释与不注释的效果
cout << hex << 123 << "<end>" << endl;
//使用控制符
cout << "<start>"
<< setw(30)
<< setfill('*')
<< setiosflags(ios::showbase)//基数
<< setiosflags(ios::internal)
<< hex
<< 123
<< "<end>"
<< endl;
}
/*
使用控制符(又称操纵符)输出 需要添加iomanip头文件
dec hex oct
ws:用于在输入的时候跳过空白符
endl:输出一个换行符并刷新输出流
ends:插入一个空字符null,通常用来结束一个字符串
flush:刷新一个输出流
setbase(n):设置整数的基数为n,n的取值0,8,10,16 ,默认为0,十进制
setfill(c):设置c为填充空格,默认为空格
setw(n):设置域宽为n
setprecision(n):设置实数的精度为n 位
在以一般十进制小数形式输出时,n代表有效数字
在以fixed(固定小数位数)形式和scientific(指数)形式输出时,n为小数位数
setiosflags(f):设置参数f指定的状态标识
resetiosflags(f):终止参数f指定的状态标识
setiosflags(ios::left) 数据按域宽 左对齐 输出
setiosflags(ios::right) 数据按域宽 右对齐 输出
setiosflags(ios::fixed) 固定的小数位数显示
setiosflags(ios::scientific) 设置浮点数以科学计数法(即指数形式)显示
setiosflags(ios::showpos) 在正数前添加一个‘+’号
setiosflags(ios::uppercase) 在以科学计数法E和十六进制输出字母时用大写表示
setiosflags(ios::skipws) 忽略前导空格
setiosflags(ios::internal)数值的符号位在域宽内左对齐,数值右对齐,中间由填充字符填充
*/
void test03()
{
int a;
cout << "input a:";
cin >> a;
//以十进制形式输出整数
cout << "dec:" << dec << a << endl;
//以十六进制形式输出整数
cout << "hex:" << hex << a << endl;
//以八进制形式输出整数a
cout << "oct:" << setbase(8) << a << endl;
char *pt = "china";
//指定域宽为10,输出字符串
cout << setw(10) << pt << endl;
//setw(n)指定域宽,输出字符串,空白处以'*'填充setfill(c)
cout << setfill('*') << setw(10) << pt << endl;
double pi = 22.0 / 7.0;//计算pi值
cout << "pi = " << setiosflags(ios::fixed) << pi << endl;
//按指数形式输出8位小数
cout << setiosflags(ios::scientific) << setprecision(8);
cout << "pi = " << pi << endl;//输出pi值
//按指数形式输出4位小数
cout << "pi = " << setprecision(4) << pi << endl;
//改为小数形式输出 注意resetiosflags(f):终止参数f指定的状态标识
cout << "pi = " << setiosflags(ios::fixed) << pi << endl;
}
void test04()
{
double a = 123.456, b = 3.14159, c = -3214.67;
cout << setiosflags(ios::fixed) << setiosflags(ios::right) << setprecision(2);
cout << setw(10) << a << endl;
cout << setw(10) << b << endl;
cout << setw(10) << c << endl;
}
void test05()
{
int a = 21;
cout.setf(ios::showbase); //显示基数符号(0x或)
cout << "dec:" << a << endl; //默认以十进制形式输出a
cout.unsetf(ios::dec); //终止十进制的格式设置
cout.setf(ios::hex); //设置以十六进制输出的状态
cout << "hex:" << a << endl; //以十六进制形式输出a
cout.unsetf(ios::hex); //终止十六进制的格式设置
cout.setf(ios::oct); //设置以八进制输出的状态
cout << "oct:" << a << endl; //以八进制形式输出a
cout.unsetf(ios::oct);
char *pt = "China"; //pt指向字符串"China"
cout.width(10); //指定域宽为
cout << pt << endl; //输出字符串
cout.width(10); //指定域宽为
cout.fill('*'); //指定空白处以'*'填充
cout << pt << endl; //输出字符串
double pi = 22.0 / 7.0; //输出pi值
cout.setf(ios::scientific); //指定用科学记数法输出
cout << "pi="; //输出"pi="
cout.width(14); //指定域宽为
cout << pi << endl; //输出pi值
cout.unsetf(ios::scientific); //终止科学记数法状态
cout.setf(ios::fixed); //指定用定点形式输出
cout.width(12); //指定域宽为
cout.setf(ios::showpos); //正数输出“+”号
cout.setf(ios::internal); //数符出现在左侧
cout.precision(6); //保留位小数
cout << pi << endl; //输出pi,注意数符“+”的位置
}
void test()
{
//test01();
//test02();
//test03();
//test04();
test05();
}
#endif
/*
文件I/O
1、输入输出是以系统指定的标准设备(输入设备为键盘,输出设备为显示器)为对象的。
在实际应用中,常以磁盘文件作为对象。即从磁盘文件读取数据,将数据输出到磁盘文件。
2、输入输出类主要在fstream.h这个头文件,定义了三个类,控制对文件的各种输入输出操作,
他们分别是ifstream、ofstream、fstream,其中fstream类是由iostream类派生而来,
3、 ifstream类,它是从istream类派生的,用来支持从磁盘文件的输入。
ofstream类,它是从ostream类派生的,用来支持向磁盘文件的输出。
fstream类,它是从iostream类派生的,用来支持对磁盘文件的输入输出
4、
输出流用来写文件 out--->输出文件
输入流用来读文件 in <---输入文件
*/
#if 0
#include<fstream>
void test01()
{
char *fname = "d:/11.txt";
ofstream fout(fname, ios::app);//建一个 输出流对象 和文件关联;
if (!fout)
{
cout << "打开文件失败" << endl;
return;
}
fout << "hello...111" << endl;
fout << "hello...222" << endl;
fout << "hello...333" << endl;
fout.close();
//读文件
ifstream fin(fname);//建立一个输入流对象 和文件关联
char ch;
while (fin.get(ch))
{
cout << ch;
}
fin.close();
}
class Teacher
{
public:
Teacher()
{
age = 0;
strcpy(name, "");
}
Teacher(int _age, char *_name)
{
age = _age;
strcpy(name, _name);
}
void prinT()
{
cout << "age:" << age << "name:" << name << endl;
}
private:
int age;
char name[32];
};
/*
二进制文件
对二进制文件的读写主要用istream类的成员函数read和write来实现。
这两个成员函数的原型为
istream& read(char *buffer,int len);
ostream& write(const char * buffer,int len);
*/
void test02()
{
char *fname = "d:/2a.dat";
//读文件
ofstream fout(fname, ios::binary);//建一个 输出流对象 和文件关联;
if (!fout)
{
cout << "打开文件失败" << endl;
return;
}
Teacher t1(31, "t31");
Teacher t2(32, "t32");
fout.write((char*)&t1, sizeof(t1));
fout.write((char*)&t2, sizeof(t2));
fout.close();
//写文件
ifstream fin(fname);//建立一个输入流对象 和文件关联
Teacher tmp;
fin.read((char*)&tmp, sizeof(Teacher));
tmp.prinT();
fin.read((char*)&tmp, sizeof(Teacher));
tmp.prinT();
fin.close();
}
void test03()
{
char fileName[80];
char buffer[255];
cout << "请输入一个文件名:";
cin >> fileName;//从屏幕读取数据到fileName
//写文件
ofstream fout(fileName, ios::app);
fout << "1111111111111\n";
fout << "2222222222222\n";
cin.ignore(1, '\n');
cin.getline(buffer, 255);//将屏幕上读取的数据写入 buffer
fout << buffer << "\n";//将buffer中的数据写入文件
fout.close();
//读文件
ifstream fin(fileName);
cout << "Here's the content of the file:\n";
char ch;
while (fin.get(ch))
cout << ch;
cout << "\n*** End of file contents.***\n";
fin.close();
}
/*
在定义ifstream和ofstream类对象的时候,我们也可以不指定文件。
以后可以通过成员函数open()显式的把一个文件连接到一个类对象上
利用ofstream类对象,将内容写入到文件中
*/
void test04()
{
ofstream myfile;//定义一个输出类对象
myfile.open("d:/1.txt", ios::app | ios::out);
if (!myfile)
{
cout << "打开文件失败" << endl;
}
myfile << "传智播客" << endl << "网址: " << "www.itcast.cn";
myfile.close();
}
/*
利用ifstream类对象,将文件中的数据读取出来,然后再输出到标准设备
*/
void test05()
{
ifstream myfile;//定义一个输入类对象
myfile.open("d:/2.txt", ios::app|ios::in);
if (!myfile)
{
cout << "文件读错误";
system("pause");
exit(1);
}
char ch;
string content;
while (myfile.get(ch))//get()函数读到文件末尾返回假值
{
content += ch;
cout.put(ch);//cout << ch;
}
myfile.close();
cout << content;
}
/*
利用fstream类对象可以同对文件进行读写操作
*/
void test06()
{
fstream myfile;
//写文件
myfile.open("d:/2.txt", ios::out|ios::app);
if (!myfile)
{
cout << "文件写错误,文件属性可能为只读" << endl;
system("pause");
exit(1);
}
myfile << "船只" << "sdfg" << endl;
myfile.close();
//读文件
myfile.open("d:/2.txt", ios::in|ios::app);
if (!myfile)
{
cout << "文件读错误,文件可能丢失" << endl;
system("pause");
exit(1);
}
char ch;
while (myfile.get(ch))
{
cout.put(ch);
}
myfile.close();
}
void test()
{
//test01();
//test02();
//test03();
//test04();
//test05();
test06();
}
#endif
#if 0
#include<fstream>
/*
编程实现以下数据输入/输出:
(1)以左对齐方式输出整数,域宽为12。
(2)以八进制、十进制、十六进制输入/输出整数。
(3)实现浮点数的指数格式和定点格式的输入/输出,并指定精度。
(4)把字符串读入字符型数组变量中,从键盘输入,要求输入串的空格也全部读入,以回车符结束。
(5)将以上要求用流成员函数和操作符各做一遍。
*/
void test01()
{
long a = 234;
double b = 2345.67890;
char c[100];
//流成员函数实现
cout.fill('*');
cout.flags(ios_base::left);
cout.width(12);
cout << a << endl;
//---------------------
cout.fill('*');
cout.flags(ios::right);
cout.width(12);
cout << a << endl;
cout.flags(ios::hex);
cout << 234 << '\t';
cout.flags(ios::dec);
cout << 234 << '\t';
cout.flags(ios::oct);
cout << 234 << endl;
//------
cout.flags(ios::scientific);
cout << b << '\t';
cout.flags(ios::fixed);
cout << b << endl;
cin.get(c, 99);
cout << c << endl;
//操作符实现
cout << setfill('*');
cout << left << setw(12) << a << endl;
cout << right << setw(12) << a << endl;
cout << hex << a << '\t' << dec << a << '\t' << oct << a << endl;
cout << scientific << b << '\t' << fixed << b << endl;
}
/*
将两个文件合并成一个文件。
*/
void test02()
{
int i = 1;
char c[1000];
ifstream ifile1("d:/1.cpp");
ifstream ifile2("d:/2.cpp");
ofstream ofile("d:/3.cpp");
//流对象的成员函数eof的值为非0值(一般设为1),表示文件结束 了。
while (!ifile1.eof())
{
ifile1.getline(c, 999);//C++ getline()函数读入一行字符
ofile << c << endl;
}
while (!ifile2.eof())
{
ifile2.getline(c, 999);
ofile << c << endl;
}
ifile1.close();
ifile2.close();
ofile.close();
}
/*
统计一篇英文文章中单词的个数与行数
*/
//isalpha() 判断字符ch 是否为英文字母,若为英文字母
//返回非0(小写字母2 大写字母1),不是字母返回0
#include<cctype>
bool isalph(char);
bool isalph(char c){
return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
}
void test03()
{
ifstream ifile("d:/2.cpp");
if (!ifile)
{
cout << "读文件失败" << endl;
}
char text[1000];
bool inword = false;
int rows = 0, words = 0;
int i;
while (!ifile.eof())
{
ifile.getline(text, 999);//读一行
rows++;
i = 0;
while (text[i] != 0)
{
if (!isalph(text[i]))//检测到空格 hello world
inword = false;
else if (isalph(text[i]) && inword == false)
{
words++;
inword = true;
}
i++;
}
}
cout << "rows = " << rows << endl;
cout << "words = " << words << endl;
ifile.close();
}
/*
将C++源程序每行前加上行号与一个空格
*/
void test04()
{
int i = 1;
char c[1000];
ifstream ifile("d:/1.cpp");//读文件
ofstream ofile("d:/2.cpp");//写文件
while (!ifile.eof())
{
ofile << i++ << ":";
ifile.getline(c, 999);
ofile << c << endl;
}
ifile.close();
ofile.close();
}
/*
输出 ASCII码值从20到127的ASCII码字符表,格式为每行10个
*/
void test05()
{
int i, j =0;
for (i = 32; i < 127; i++)
{
cout << char(i) << " ";
j++;
if (j % 10 == 0)
cout << endl;
}
}
void test()
{
//test01();
//test02();
//test03();
//test04();
test05();
}
#endif
/*
STL模板基本概念
algorithm(算法)、container(容器)和iterator(迭代器)
1、STL的一个重要特点是数据结构和算法的分离
*/
#include <vector>
#include <algorithm>
#if 0
//容器装 基础数据类型变量
void test01()
{
vector<int> v1;//容器 把你的元素copy到容器中
v1.push_back(-1);
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
v1.push_back(3);
// 1 3 5
//▲
//迭代器:相当于一个指针
for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
{
cout << *it << " ";
}
//3 算法 算法和迭代器 进行无缝的连接
int num1 = count(v1.begin(), v1.end(), 3);//计算元素3 在数组中的个数
cout << "num1: " << num1 << endl;
}
/*
由于容器元素的存放是按值复制的方式进行的,所以此时Teacher
必须提供Teacher的拷贝构造函数,以保证Teacher对象间拷贝正常。
*/
class Teacher
{
public:
int age;
char name[64];//若是指针,必须考虑深拷贝和浅拷贝问题
public:
void prinT()
{
cout << "age: " << age << endl;
}
};
//容器装 类对象
void test02()
{
Teacher t1, t2, t3;
t1.age = 31;
t2.age = 32;
t3.age = 33;
vector<Teacher> v1;//1容器 把你的元素copy到容器中 容器实现了数据类型和算法的有效分离
v1.push_back(t1);
v1.push_back(t2);
v1.push_back(t3);
// 1 3 5
//▲
//迭代器:相当于一个指针
for (vector<Teacher>::iterator it = v1.begin(); it != v1.end(); it++)
{
cout << it->age<< " ";
}
//3 算法 算法和迭代器 进行无缝的连接
//int num1 = count(v1.begin(), v1.end(), 3);
//cout << "num1: " << num1 << endl;
//抛砖: 如何求 容器 中 老师结点的 年龄=33 个数....
}
//容器中装 类对象指针
void test03()
{
Teacher t1, t2, t3;
t1.age = 31;
t2.age = 32;
t3.age = 33;
Teacher *p1, *p2, *p3;
p1 = &t1;
p2 = &t2;
p3 = &t3;
//1容器: 把t1 t2 t3内存首地址 放入到了容器
vector<Teacher *> v1;
v1.push_back(p1);
v1.push_back(p2);
v1.push_back(p3);
// p1 p2 p3
// ▲
//2迭代器: 相当于一个指针 分类
for (vector<Teacher*>::iterator it = v1.begin(); it != v1.end(); it++)
{
cout << (*it)->age << endl;//注意是二级指针
}
}
void test()
{
//test01();
//test02();
test03();
}
#endif
/*
STL中的string
string是STL的字符串类型,通常用来表示字符串
string是一个类, char*是一个指向字符的指针。
string封装了char*,管理这个字符串,是一个char*型的容器
*/
#if 0
/*
默认构造函数:
string(); //构造一个空的字符串string s1。
拷贝构造函数:
string(const string &str); //构造一个与str一样的string。如string s1(s2)。
带参数的构造函数
string(const char *s); //用字符串s初始化
string(int n,char c); //用n个字符c初始化
*/
//string 的初始化
void test01()
{
string s0 = string(); //构造一个空串
string s1 = "aaaa"; //初始化法1
string s2("bbbb"); //初始化法2
string s3 = s2; //通过copy构造函数
string s4(10, 'n'); //用10个字符n初始化10
cout << "s1:" << s1 << endl;
cout << "s2:" << s2 << endl;
cout << "s3:" << s3 << endl;
cout << "s4:" << s4 << endl;
}
/*
string 的遍历 返回字符串中的第n个字符
string类的字符操作:
const char & operator[] (int n) const;//最后一个const修饰的是this指针
const char & at(int n) const;
char & operator[] (int n);
char &at(int n);
perator[]和at()均返回当前字符串中第n个字符,但二者是有区别的。
主要区别在于at()在越界时会抛出异常,[]在刚好越界时会返回(char)0,
再继续越界时,编译器直接出错。
如果你的程序希望可以通过try,catch捕获异常,建议采用at()
*/
void test02()
{
string s1 = "abcdefg";
//1 数组方式
for (int i = 0; i < s1.length(); i++)
{
cout << s1[i] << " ";
}
cout << endl;
//2 迭代器
for (string::iterator it = s1.begin(); it != s1.end(); it++)
{
cout << *it << " ";
}
cout << endl;
try
{
for (int i = 0; i < s1.length() + 3; i++)
{
cout << s1.at(i) << " ";
}
}
catch (...)
{
cout << "发生异常" << endl;
}
cout << "at之后" << endl;
/*
try
{
for (int i=0; i<s1.length() + 3; i++)
{
cout << s1[i] << " "; //出现错误 不向外面抛出异常 引起程序的中断
}
}
catch ( ... )
{
cout << "发生异常\n" ;
}
*/
}
/*
字符指针和string 的转换
1、从string取得const char*的操作
const char *c_str() const; //返回一个以'\0'结尾的字符串的首地址
2、把string拷贝到char*指向的内存空间的操作
int copy(char *s, int n, int pos=0) const;
把当前串中以pos开始的n个字符拷贝到以s为起始位置的字符数组中,
返回实际拷贝的数目。
注意要保证s所指向的空间(要分配内存)足够大以容纳当前字符串,
不然会越界
*/
void test03()
{
//1 char * ===>string
string s1 = "hello world";
//2 string ===>char *
printf("s1: %s\n", s1.c_str());//漏出指针
//3 s1的内容 copy到buf中
char buf1[128] = {0};
s1.copy(buf1, 3, 0);//注意 只给你copy 3 个字符串 不会变成c风格的字符串
cout << "buf1: " << buf1 << endl;//打印hel
}
/*
字符串的连接
string &operator+=(const string &s); //把字符串s连接到当前字符串结尾
string &operator+=(const char *s); //把字符串s连接到当前字符串结尾
string &append(const char *s); //把字符串s连接到当前字符串结尾
string &append(const char *s,int n); //把字符串s的前n个字符连接到当前字符串结尾
string &append(const string &s); //同operator+=()
string &append(const string &s,int pos, int n);//把字符串s中从pos开始的n个字符连接到当前字符串结尾
string &append(int n, char c); //在当前字符串结尾添加n个字符c
*/
void test04()
{
string s0 = "7890";
string s1 = "aaa";
string s2 = "bbb";
s1 = s1 + s2;//法1
s1 += s0;
s1+=("haha");
cout << "s1:" << s1 << endl;
string s3 = "333";
string s4 = "444";
string s5 = "asdfghk";
s3.append(s4);
s3.append(s5, 0, 3); //把字符串s5中从0开始的3个字符连接到当前字符串结尾
s3.append(3, '*'); //在当前字符串结尾添加3个字符*
s3.append("zxcvbn", 3);把字符串s的前n个字符连接到当前字符串结尾
cout << "s3: " << s3 << endl;
}
/*
字符串的查找和替换 重点
查找
int find(char c,int pos=0) const; //从pos开始查找 字符c 在当前字符串的位置
int find(const char *s, int pos=0) const; //从pos开始查找 字符串s 在当前字符串的位置,如果找到,则返回
该字符串首次出现时其首字符的索引;否则返回string::npos
int find(const string &s, int pos=0) const; //从pos开始查找 字符串s 在当前字符串的位置
find函数如果查找不到,就返回-1
int rfind(char c, int pos=npos) const; //从pos开始从后向前查找字符c在当前字符串中的位置
int rfind(const char *s, int pos=npos) const;
int rfind(const string &s, int pos=npos) const;
rfind是反向查找的意思,如果查找不到, 返回-1
替换
string &replace(int pos, int n, const char *s); //删除从pos开始的n个字符,然后在pos处插入串s
string &replace(int pos, int n, const string &s); //删除从pos开始的n个字符,然后在pos处插入串s
void swap(string &s2); //交换当前字符串与s2的值
*/
void test05()
{
string s1 = "wbm hello wbm 111 wbm 222 wbm 333 ";
// ▲
//第一次 出现wbm index
int index = s1.find("wbm", 0);//位置从下标0开始查找字符串第一次出现的位置
cout << "index: "<< index << " ";
//案例1 求wbm出现的次数 每一次出现的数组下标
int offindex = s1.find("wbm", 0);
while (offindex != string::npos)
{
cout << "offindex:" << offindex << endl;
offindex = offindex + 1;
offindex = s1.find("wbm", offindex);
}
//案例2 把小写wbm 替换 为 WBM
string s3 = "aaa bbb ccc";
s3.replace(0, 3, "AAA");//删除从0位置开始的3个字符,然后用AAA代替
cout << "s3: " << s3 << endl;
offindex = s1.find("wbm", 0);
while (offindex != string::npos)
{
cout << "offindex:" << offindex << endl;
s1.replace(offindex, 3, "WBM");
offindex = offindex + 1;
offindex = s1.find("wbm", offindex);
}
cout << "s1替换后的结果: " << s1 << endl;
}
/*
截断(区间删除)和插入
string &insert(int pos, const char *s);
string &insert(int pos, const string &s);
//前两个函数在pos位置插入字符串s
string &insert(int pos, int n, char c); //在pos位置 插入n个字符c
string &erase(int pos=0, int n=npos); //删除pos开始的n个字符,返回修改后的字符串
*/
void test06()
{
string s1 = "HELLO1 HELLO2 HELLO3";
string::iterator it = find(s1.begin(), s1.end(), 'L');//使用算法find()
if (it != s1.end())
{
s1.erase(it);//删除指针所指向字符 删除一个字符
}
cout << "s1删除L后的结果: " << s1 << endl;
s1.erase(s1.begin(), s1.end());
cout << "s1 全部删除: " << s1 << endl;
cout << "s1 长度: " << s1.length() << endl;
string s2 = "BBB";
s2.insert(0, "AAA");//头插法
s2.insert(s2.length(), "CCC");//尾插法
cout << "s2" << s2 << endl;
}
void test07()
{
string s1 = "aaaBBB";
//transform() 第四个参数可以是 函数的入口地址 函数对象 预定义的函数对象
//第三个参数是转换的结果放在那里,也可以指定别的内存空间
transform(s1.begin(), s1.end(),s1.begin(), toupper);//使用预定义的函数对象
cout << "s1: " << s1 << endl;
string s2 = "AAAbbbbbb";
transform(s2.begin(), s2.end(), s2.begin(), tolower);
cout << "s2: " << s2 << endl;
}
void test()
{
//test01();
//test02();
//test03();
//test04();
//test05();
//test06();
test07();
}
#endif
/*
vector操练
1、vector是将元素置于一个动态数组中加以管理的容器。
vector可以随机存取元素(支持索引值直接存取, 用[]操作符或at()方法)。
vector尾部添加或移除元素非常快速。但是在中部或头部插入元素或移除元素比较费时
2、vector对象的默认构造
vector采用模板类实现,vector对象的默认构造形式
vector<T> vecT;
vector<int> vecInt; //一个存放 int 的vector容器。
vector<float> vecFloat; //一个存放 float 的vector容器。
vector<string> vecString; //一个存放 string 的vector容器。
... //尖括号内还可以设置指针类型或自定义类型。
Class CA{};
vector<CA*> vecpCA; //用于存放CA对象的指针的vector容器。
vector<CA> vecCA; //用于存放CA对象的vector容器。
注意:由于容器元素的存放是按值复制的方式进行的,所以此时CA必须提供CA的
拷贝构造函数,以保证CA对象间拷贝正常。
*/
#if 0
/*
数组元素的添加和删除
*/
void test01()
{
vector<int> v1;
cout << "length: " << v1.size() << endl;//获取数组的大小
v1.push_back(1);//向数组的尾部添加元素
v1.push_back(2);
v1.push_back(3);
cout << "length: " << v1.size() << endl;//获取数组的大小
cout << "头部元素:" << v1.front() << endl;//获取头部元素
//v1.front()获取头部元素,既可以当左值,也可以当右值
//v1.back() 获取头部元素,既可以当左值,也可以当右值
//函数返回值当左值 应该返回一个引用
v1.front() = 11; //修改头部元素
v1.back() = 55; //修改尾部元素
while (v1.size() > 0)
{
cout << "尾部元素" << v1.back() << endl;//获取尾部元素
v1.pop_back();//删除尾部元素
}
}
/*
vector的初始化
vector(beg,end); //构造函数将[beg, end)区间中的元素拷贝给本身。
注意该区间是左闭右开的区间。
vector(n,elem); //构造函数将n个elem拷贝给本身。
vector(const vector &vec); //拷贝构造函数
*/
void test02()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
v1.push_back(7);
vector<int> v2 = v1;//对象初始化
vector<int> v3(v1.begin(), v1.begin() + 3);//0+3 =3 0 1 2 共3个元素
vector<int> v4(v1.begin(), v1.end());// [begin, end)
vector<int> v5(v1);
vector<int> v6(3,5);//容器v6存在3 个元素,每个元素都是5
vector<int> va, vb, vc, vd;
int iarray[] = { 0, 1, 2, 3, 4 };
va.assign(iarray, iarray + 5);//注意是[iarray, iarray + 5) 左闭右开区间
vb.assign(va.begin(), va.end());
vc.assign(3, 9);//将3 个元素9 赋值给本身
vd = va;
va.swap(vd);//将vd与va的元素互换
}
/*
vector的遍历 通过数组的方式
*/
void printV(vector<int> &v)
{
for (int i = 0; i < v.size(); i++)
{
cout << v[i] << " ";
}
}
/*
vector.size(); //返回容器中元素的个数
vector.empty(); //判断容器是否为空
vector.resize(num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除。
vector.resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除
*/
void test03()
{
vector<int> v1(10);//提前把内存准备好
for (int i = 0; i < 10; i++)
{
v1[i] = i + 1;//[] 重载了
}
for (int i = 0; i < 10; i++)
{
cout << v1[i] << " ";//[] 重载了
}
printV(v1);
vector<int> v2;
v2.assign(v1.begin(), v1.begin() + 3);
int isize = v2.size();
cout << "容器的大小: "<<v2.size() << endl;
bool bempty = v2.empty();
cout << "bempty = " << bempty;
v2.resize(5);
printV(v2);
v2.resize(8,66);
printV(v2);
}
/*
push_back 的强化记忆
*/
void test04()
{
vector<int> v1(10);//提前把内存准备好,默认初始化为0
v1.push_back(100); //在数组后添加
v1.push_back(200);
cout << "size: " << v1.size() << endl;
printV(v1);
}
/*
vector 迭代器
1迭代器 end()的理解
1 3 5
▲
▲
当 it == v1.end()的时候 说明这个容器已经遍历完毕了...
注意:end()的位置 应该是 5的后面
2 迭代器的种类
typedef iterator pointer;
typedef const_iterator const_pointer;
typedef _STD reverse_iterator<iterator> reverse_iterator;
typedef _STD reverse_iterator<const_iterator> const_reverse_iterator;
*/
void test05()
{
vector<int> v1(10);
for (int i = 0; i < 10; i++)
{
v1[i] = i + 1;
}
//正向遍历
for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
{
cout << *it << " ";
}
cout << endl<<"------------" << endl;
//逆向遍历
for (vector<int>::reverse_iterator rit = v1.rbegin(); rit != v1.rend(); rit++)
{
cout << *rit << " ";
}
}
/*
vector 删除 重点
vector.clear(); //移除容器的所有数据
vec.erase(beg,end); //删除[beg,end)区间的数据,返回下一个数据的位置。
vec.erase(pos); //删除pos位置的数据,返回下一个数据的位置
vector 的插入
vector.insert(pos,elem); //在pos位置插入一个elem元素的拷贝,返回新数据的位置。
vector.insert(pos,n,elem); //在pos位置插入n个elem数据,无返回值。
vector.insert(pos,beg,end);//在pos位置插入[beg,end)区间的数据,无返回值
*/
void test06()
{
vector<int> v1(10);
for (int i = 0; i < 10; i++)
{
v1[i] = i + 1;
}
//区间删除
v1.erase(v1.begin(), v1.begin() + 3);
printV(v1);
//根据元素的位置 指定位置删除
v1.erase(v1.begin());//在头部删除一个元素
printV(v1);
cout << endl;
//根据元素的值删除
v1[1] = 2;
v1[3] = 2;
printV(v1);
for (vector<int>::iterator it = v1.begin(); it != v1.end();)//这里不能有it++了
{
if (*it == 2)
{
//当删除迭代器所指向的元素的时候,erase删除函数会让
//it自动下移动,返回新的位置
it = v1.erase(it);
}
else
{
it++;
}
}
printV(v1);
cout << endl;
//在开头添加100, 在末尾添加200
v1.insert(v1.begin(), 100);
v1.insert(v1.end(), 200);
printV(v1);
cout << endl;
//在开头添加2个 99
v1.insert(v1.begin(), 2, 99);
printV(v1);
cout << endl;
//在开头添加[ v1.begin()+3, v1.end() )区间的数据
v1.insert(v1.begin(), v1.begin()+3, v1.end());
printV(v1);
cout << endl;
//在开头删除[v1.begin(), v1.begin() + 3) 区间的数据
v1.erase(v1.begin(), v1.begin() + 3);
printV(v1);
cout << endl;
//删除 v1.begin() + 2 位置的数据
v1.erase(v1.begin() + 5);
printV(v1);
cout << endl;
//移除容器所有数据
v1.clear();
printV(v1);
cout << endl;
}
/*
vector 数据存取
vec.at(idx); //返回索引idx所指的数据,如果idx越界,抛出out_of_range异常。
vec[idx]; //返回索引idx所指的数据,越界时,运行直接报错
*/
void test07()
{
vector<int> v1(10);
for (int i = 0; i < 10; i++)
{
v1[i] = i + 1;
}
try
{
v1.at(2) = 111;
v1[5] = 555;
}
catch (out_of_range &v1)
{
cout << "数组越界" << endl;
}
printV(v1);
try
{
v1.at(11) = 111;//抛异常
//v1[12] = 555;//程序崩溃
}
catch (out_of_range &v1)
{
cout << "数组越界" << endl;
}
printV(v1);
}
void test()
{
//test01();
//test02();
//test03();
//test04();
//test05();
test06();
//test07();
}
#endif
#include<deque>
/*
deque 即双端队列。
(deque,全名double-ended queue)是一种具有队列和栈的性质的数据结构。
双端队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行。
deque容器
deque是“double-ended queue”的缩写,和vector一样都是STL的容器,deque是双端数组,而vector是单端的。
deque在接口上和vector非常相似,在许多操作的地方可以直接替换。
deque可以随机存取元素(支持索引值直接存取, 用[]操作符或at()方法,这个等下会详讲)。
deque头部和尾部添加或移除元素都非常快速。但是在中部安插元素或移除元素比较费时
*/
#if 0
void printD(deque<int> &d)
{
for (deque<int>::iterator it = d.begin(); it != d.end(); it++)
{
cout << *it << " ";
}
}
void test01()
{
deque<int> dl;
dl.push_back(1);
dl.push_back(3);
dl.push_back(5);
dl.push_front(-11);
dl.push_front(-12);
dl.push_front(-33);
dl.push_front(-55);
dl.push_front(-75);
cout << endl;
cout << "头部元素:" << dl.front() << endl;
cout << "尾部元素:" << dl.back() << endl;
printD(dl);
cout << endl;
dl.pop_front(); //删除头部元素
dl.pop_back(); //删除尾部元素
printD(dl);
cout << endl;
//查找 -33在数组下标的值
deque<int>::iterator it = find(dl.begin(), dl.end(), -33);
if (it != dl.end())
{
cout << "-33 的数组下标是" << distance(dl.begin(), it) << endl;
}
else
{
cout << "没有找到值为 -33 的元素" << endl;
}
}
/*
//que.assign(beg,end) 将[beg; end)区间中的数据赋值给que。
//que.assign(n,elem) 将n个elem的拷贝赋值给que。
//que.at(idx) 传回索引idx所指的数据,如果idx越界,抛出out_of_range。
//que.rbegin() 返回一个逆序迭代器,它指向容器que的最后一个元素。
//que.rend() 返回一个逆序迭代器,它指向容器que的第一个元素的前一个位置。
//que.resize(num) 重新指定队列的长度。
//que.swap(que2) 交换容器que和que2中的所有元素。
//swap(que1,que2) 交换容器que1和que2中的所有元素。
//que.erase(pos) 删除pos位置的数据,传回下一个数据的位置。
//que.erase(beg,end) 删除[beg,end)区间的数据,传回下一个数据的位置。
//que.insert(pos,elem) 在pos位置插入一个elem拷贝,传回新数据位置
//que.insert(pos,beg,end) 在pos位置插入在[beg,end)区间的数据。无返回值
que.back() 返回容器que的最后一个元素的引用。如果que为空,则该操作未定义。
que.begin() 传回迭代器中的第一个数据地址。
que.clear() 移除容器中所有数据。
que.empty() 判断容器是否为空。
que.end() 返回一个迭代器,它指向容器que的最后一个元素的下一位置。
que.front() 返回容器que的第一个元素的引用。如果que为空,则该操作为空。
que.insert(pos,n,elem) 在pos(迭代器)位置插入>n个elem数据。无返回值
que.max_size() 返回容器que可容纳的最多元素个数。
que.pop_back() 删除最后一个数据。
que.pop_front() 删除头部数据。
que.push_back(elem) 在尾部加入一个数据。
que.push_front(elem) 在头部插入一个数据。
que.size() 返回容器中实际数据的个数。
*/
//数据存取
void test02()
{
deque<int> d;
d.push_back(1);
d.push_back(3);
d.push_back(5);
d.push_back(7);
d.push_back(9);
int da0 = d.at(0);
int da1 = d[1];
cout << "da0 :" << da0 << endl;
cout << "da1 :" << d[1] << endl;
d.at(0) = 99;
d[1] = 88;
printD(d);
int dfront = d.front();
int dback = d.back();
d.front() = 77;
d.back() = 66;
printD(d);
}
/*
deque 与迭代器
deque.begin(); //返回容器中第一个元素的迭代器。
deque.end(); //返回容器中最后一个元素 之后 的迭代器。
deque.rbegin(); //返回容器中倒数第一个元素的迭代器。
deque.rend(); //返回容器中倒数最后一个元素之后的迭代器
*/
void test03()
{
deque<int> d;
d.push_back(1);
d.push_back(3);
d.push_back(5);
d.push_back(7);
d.push_back(9);
for (deque<int>::iterator it = d.begin(); it != d.end(); it++)
{
cout << *it << " ";
}
cout << endl;
for (deque<int>::reverse_iterator it = d.rbegin(); it != d.rend(); it++)
{
cout << *it << " ";
}
}
/*
deque 的带参数构造
deque(beg,end); //构造函数将[beg, end)区间中的元素拷贝给本身。注意该区间是左闭右开的区间。
deque(n,elem); //构造函数将n个elem拷贝给本身。
deque(const deque &deq); //拷贝构造函数
*/
void test04()
{
deque<int> d;
d.push_back(1);
d.push_back(3);
d.push_back(5);
d.push_back(7);
d.push_back(9);
deque<int> db(d.begin(), d.end());
printD(db);
cout << endl;
deque<int>dc(5, 8);
printD(dc);
cout << endl;
deque<int> f(d);
printD(f);
cout << endl;
}
/*
deque 的赋值
deque.assign(beg,end); //将[beg, end)区间中的数据拷贝赋值给本身。注意该区间是左闭右开的区间。
deque.assign(n,elem); //将n个elem拷贝赋值给本身。
deque& operator=(const deque &deq); //重载等号操作符
deque.swap(deq); //将vec与本身的元素互换
*/
void test05()
{
deque<int> d, da, db, dc,df;
d.push_back(1);
d.push_back(3);
d.push_back(5);
d.push_back(7);
d.push_back(9);
da.assign(d.begin(), d.begin() + 2);
printD(da);
cout << endl;
db.assign(5, 8);//将5 个8 赋值给本身
printD(db);
cout << endl;
dc = d;
printD(dc);
cout << endl;
df.swap(d);
printD(df);
cout << endl;
}
/*
deque 的大小
deque.size(); //返回容器中元素的个数
deque.empty(); //判断容器是否为空
//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除。
deque.resize(num);
//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除
deque.resize(num, elem);
*/
void test06()
{
deque<int> d;
d.push_back(1);
d.push_back(3);
d.push_back(5);
int size = d.size();
cout <<"size = "<< size << endl;
cout << d.empty() << endl;
if (!d.empty())
{
d.resize(5);
printD(d);
cout << endl;
d.resize(7, 1);
printD(d);
cout << endl;
d.resize(2);
printD(d);
cout << endl;
}
}
/*
deque 的插入
deque.insert(pos,elem); //在pos位置插入一个elem元素的拷贝,返回新数据的位置。
deque.insert(pos,n,elem); //在pos位置插入n个elem数据,无返回值。
deque.insert(pos,beg,end); //在pos位置插入[beg,end)区间的数据,无返回值
deque.clear(); //移除容器的所有数据
deque.erase(beg,end); //删除[beg,end)区间的数据,返回下一个数据的位置。
deque.erase(pos); //删除pos位置的数据,返回下一个数据的位置。
*/
void test07()
{
deque<int> d;
d.push_back(1);
d.push_back(3);
d.push_back(5);
d.push_back(6);
d.push_back(9);
d.push_back(3);
d.push_back(33);
deque<int>::iterator itbegin = d.begin() + 1;
deque<int>::iterator itend = d.begin() + 3;
d.erase(itbegin, itend);
printD(d);
for (deque<int>::iterator it = d.begin(); it != d.end();)//小括号里不需写 ++it
{
if (*it == 3)
{
it = d.erase(it);//以迭代器为参数,删除元素3,并把数据删除后的下一个元素位置返回给迭代器。
}
else
{
++it;
}
}
cout << endl;
printD(d);
d.clear();
}
void test()
{
//test01();
//test02();
//test03();
//test04();
//test05();
//test06();
test07();
}
#endif
/*
stack是堆栈容器,是一种“先进后出”的容器。
stack是简单地装饰deque容器而成为另外的一种容器。
#include <stack>
stack.push(elem);//往栈头添加元素
stack.pop(); //从栈头移除第一个元素
*/
#include <stack>
#if 0
//栈模型 先入后出
//栈的算法 和 数据类型的分离
/*
stack.top(); //返回最后一个压入栈元素
stack.empty(); //判断堆栈是否为空
stack.size(); //返回堆栈的大小
*/
void test01()
{
stack<int> s;
//入栈
for (int i = 0; i < 10; i++)
{
s.push(i + 1);
}
cout << "栈的大小" << s.size() << endl;
//出栈
while (!s.empty())
{
int tmp = s.top();//获取栈顶元素
cout << tmp << " ";
s.pop();//弹出栈顶元素
}
}
class Teacher
{
public:
int age;
char name[32];
public:
void printT()
{
cout << "age: " << age << endl;
}
};
void test02()
{
Teacher t1, t2, t3;
t1.age = 11;
t2.age = 32;
t3.age = 33;
stack<Teacher> s;
s.push(t1);
s.push(t2);
s.push(t3);
while (!s.empty())
{
Teacher tmp = s.top();
tmp.printT();
s.pop();
}
}
void test03()
{
Teacher t1, t2, t3;
t1.age = 11;
t2.age = 32;
t3.age = 33;
stack<Teacher *> s;
s.push(&t1);
s.push(&t2);
s.push(&t3);
while (!s.empty())
{
Teacher * tmp = s.top();
tmp->printT();
s.pop();
}
}
/*
stack对象的拷贝构造与赋值
stack(const stack &stk); //拷贝构造函数
stack& operator=(const stack &stk); //重载等号操作符
*/
void test04()
{
stack<int> s;
s.push(1);
s.push(2);
s.push(3);
stack<int> sa(s);//拷贝构造
stack<int> sb;
sb = s;//赋值
}
void test()
{
//test01();
//test02();
//test03();
test04();
}
#endif
/*
queue是队列容器,是一种“先进先出”的容器。
queue是简单地装饰deque容器而成为另外的一种容器
queue采用模板类实现,queue对象的默认构造形式:queue<T> queT;
*/
#include <queue>
#if 0
/*
队列中的基本数据类型
queue.push(elem); //往队尾添加元素
queue.pop(); //从队头移除第一个元素
queue.back(); //返回最后一个元素
queue.front(); //返回第一个元素
queue.empty(); //判断队列是否为空
queue.size(); //返回队列的大小
*/
void test01()
{
queue<int> q;
q.push(1);
q.push(2);
q.push(3);
cout << "队头元素" << q.front() << endl;
cout << "队列的大小" << q.size() << endl;
while (!q.empty())
{
int tmp = q.front();//返回队头元素,不是删除
cout << tmp << " ";
q.pop();//从队头移除第一个元素
}
}
/*
队列的算法和分离
*/
class Teacher
{
public:
int age;
char name[32];
public:
void printT()
{
cout << "age:" << age << endl;
}
};
void test02()
{
Teacher t1, t2, t3;
t1.age = 31;
t2.age = 32;
t3.age = 33;
queue<Teacher> q;
q.push(t1);
q.push(t2);
q.push(t3);
while (!q.empty())
{
Teacher tmp = q.front();//返回第一个元素
tmp.printT();
q.pop();//从队头移除第一个元素
}
}
void test03()
{
Teacher t1, t2, t3;
t1.age = 31;
t2.age = 32;
t3.age = 33;
queue<Teacher *> q;
q.push(&t1);
q.push(&t2);
q.push(&t3);
while (!q.empty())
{
Teacher *tmp = q.front();
tmp->printT();
q.pop();
}
}
void test()
{
//test01();
//test02();
test03();
}
#endif
/*
list是一个 双向链表 容器,可高效地进行插入删除元素。
list不可以随机存取元素,所以不支持at.(pos)函数与[]操作符。
it++(ok) it+5(err)
list.push_back(elem); //在容器尾部加入一个元素
list.pop_back(); //删除容器中最后一个元素
list.push_front(elem); //在容器开头插入一个元素
list.pop_front(); //从容器开头移除第一个元素
list.front(); //返回第一个元素。
list.back(); //返回最后一个元素
list.begin(); //返回容器中第一个元素的迭代器。
list.end(); //返回容器中最后一个元素之后的迭代器。
list.rbegin(); //返回容器中倒数第一个元素的迭代器。
list.rend(); //返回容器中倒数最后一个元素的后面的迭代器
list(beg,end); //构造函数将[beg, end)区间中的元素拷贝给本身。注意该区间是左闭右开的区间。
list(n,elem); //构造函数将n个elem拷贝给本身。
list(const list &lst); //拷贝构造函数。
list.assign(beg,end); //将[beg, end)区间中的数据拷贝赋值给本身。注意该区间是左闭右开的区间。
list.assign(n,elem); //将n个elem拷贝赋值给本身。
list& operator=(const list &lst); //重载等号操作符
list.swap(lst); // 将lst与本身的元素互换
list.size(); //返回容器中元素的个数
list.empty(); //判断容器是否为空
list.resize(num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
list.resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除
list.insert(pos,elem); //在pos位置插入一个elem元素的拷贝,返回新数据的位置。
list.insert(pos,n,elem); //在pos位置插入n个elem数据,无返回值。
list.insert(pos,beg,end); //在pos位置插入[beg,end)区间的数据,无返回值
list.clear(); //移除容器的所有数据
list.erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置。
list.erase(pos); //删除pos位置的数据,返回下一个数据的位置。
lst.remove(elem); //删除容器中所有与elem值匹配的元素。
lst.reverse(); //反转链表
*/
#include <list>
#include<functional>//因为用了greater<int>()
#if 0
//list基本操作
void test01()
{
list<int> l;
cout << "list 的大小: " << l.size() << endl;
for (int i = 0; i < 10; i++)
{
l.push_back(i);//尾部插入元素
}
cout << "list的大小: " << l.size() << endl;
//注意迭代器的使用
list<int>::iterator it = l.begin();
while (it != l.end())
{
cout << *it << " ";
it++;//迭代器只能这么递增
}
cout << endl;
//list不能随机访问
// 0 1 2 3 4 5
// △
it = l.begin();
it++;
it++;
it++;
//it = it + 5;//不支持随机访问容器
l.insert(it, 100);//注意100插入在哪一个位置
for (list<int>::iterator it = l.begin(); it != l.end(); it++)
{
cout << *it << " ";
}
//结论 1
//链表的节点index 序号从0号位置开始
//在3号位置插入元素,让原来的3号位置变成4号位置
//原来的4号位置变成5号位置
}
//list删除
void test02()
{
list<int> l;
cout << "list 的大小: " << l.size() << endl;
for (int i = 0; i < 10; i++)
{
l.push_back(i);//尾部插入元素 尾插法
}
cout << "list 的大小: " << l.size() << endl;
//遍历list双向链表
for (list<int>::iterator it = l.begin(); it != l.end(); it++)
{
cout << *it << " ";
}
cout << endl;
//删除区间内的元素,只能按照以下做法,因为list不能随机存取
//0 1 2 3 4 5
// ▲
list<int>::iterator it1 = l.begin();
list<int>::iterator it2 = l.begin();
it2++;
it2++;
it2++;
l.erase(it1, it2);//擦除的是[0, 3) 左闭右开区间
for (list <int>::iterator it = l.begin(); it != l.end(); it++)
{
cout << *it << " ";
}
cout << endl;
l.insert(l.begin(), 100);//在队列开头插入100
l.insert(l.begin(), 100);
l.insert(l.begin(), 100);
l.insert(l.begin(), 3);
l.insert(l.end(), 3);
l.erase(l.begin());//擦除头部元素
//法1
l.remove(100);//移除所有与100相同的元素
cout << "移除所有的值为100的元素:" << endl;
for (list <int>::iterator it = l.begin(); it != l.end(); it++)
{
cout << *it << " ";
}
cout << endl<<"移除所有的值为3的元素:" << endl;
//法2
for (list<int>::iterator it = l.begin(); it != l.end();)
{
if (*it == 3)
{
it = l.erase(it);//以迭代器为参数,删除元素3,并把数据删除后的下一个元素位置返回给迭代器。
//此时, 不执行++it
}
else
{
++it;
}
}
for (list <int>::iterator it = l.begin(); it != l.end(); it++)
{
cout << *it << " ";
}
cout << endl;
l.reverse();//list反序排列
for (list <int>::iterator it = l.begin(); it != l.end(); it++)
{
cout << *it << " ";
}
}
/*
一、容器deque的使用方法
适合在头尾添加移除元素。使用方法与vector类似。
二、容器queue,stack的使用方法
适合队列,堆栈的操作方式。
三、容器list的使用方法
适合在任意位置快速插入移除元素
*/
void test()
{
//test01();
test02();
}
#endif
/*
最大值优先级队列、最小值优先级队列
优先级队列适配器 STL priority_queue
*/
#if 0
void test01()
{
priority_queue<int> p1;//默认情况下是 最大值优先级队列
priority_queue<int, vector<int>, less<int> > p2;//提前定义好的预定义函数 谓词
priority_queue<int, vector<int>, greater<int> > p3;//最小值优先队列
p1.push(33);
p1.push(11);
p1.push(55);
p1.push(22);
cout << "队头元素: " << p1.top() << endl;
cout << "队头大小: " << p1.size() << endl;
while (p1.size() > 0)
{
cout << p1.top() << " ";
p1.pop();//弹出元素
}
}
void test02()
{
priority_queue<int, vector<int>, greater<int> > p1;//现在编译器不能用了
p1.push(33);
p1.push(11);
p1.push(55);
p1.push(22);
cout << "队头元素: " << p1.top() << endl;
cout << "队头大小: " << p1.size() << endl;
while (p1.size() > 0)
{
cout << p1.top() << " ";
p1.pop();//弹出元素
}
}
void test()
{
test01();
test02();
}
#endif
/*
set与multiset
1、set是一个集合容器,其中所包含的元素是唯一的,集合中的元素按一定的顺序
排列。元素插入过程是按排序规则插入,所以不能指定插入位置。
2、set采用红黑树变体的数据结构实现,红黑树属于平衡二叉树。在插入操作和删除
操作上比vector快。
3、set不可以直接存取元素。(不可以使用at.(pos)与[]操作符)。
4、multiset与set的区别:set支持唯一键值,每个元素值只能出现一次;而multiset中
同一值可以出现多次。
5、不可以直接修改set或multiset容器中的元素值,因为该类容器是自动排序的。
如果希望修改一个元素值,必须先删除原有的元素,再插入新的元素。
*/
#include <set>
#if 0
/*
集合 元素唯一 自动排序(默认情况 从小到大)
不能按照[]方式插入元素
红黑树
*/
void test01()
{
set<int> set1;
for (int i = 0; i < 5; i++)
{
int tmp = rand();
set1.insert(tmp);
}
//插入元素重复的
set1.insert(100);//只能插入一次
set1.insert(100);
set1.insert(100);
//使用迭代器遍历
for (set<int>::iterator it = set1.begin(); it != set1.end(); it++)
{
cout << *it << " ";
}
cout << endl;
//删除集合
while (!set1.empty())
{
set<int>::iterator it = set1.begin();//找到头部元素
cout << *it << " ";
set1.erase(set1.begin());//使用erase()使it自动下移
}
cout <<"集合大小:"<< set1.size() << endl;
}
/*
对基本的数据类型,set能够自动的排序
*/
void test02()
{
set<int> set1;
set<int, less<int>> set2; //默认情况从小到大
set<int, greater<int>>set3; //#include<functional>//因为用了greater<int>()
for (int i = 0; i < 5; i++)
{
int tmp = rand();
set3.insert(tmp);
}
//从大到小
for (set<int>::iterator it = set3.begin(); it != set3.end(); it++)
{
cout << *it << " ";
}
}
/*
对自定义类型数据进行排序
访函数
*/
class Student
{
public:
Student(char *name, int age)
{
strcpy(this->name, name);
this->age = age;
}
public:
char name[64];
int age;
};
/*
函数对象functor的用法
1、C++提供了一个重要的实现回调函数的方法,那就是函数对象。
functor,翻译成 函数对象,伪函数,运算符,是重载了“()”
操作符的普通类对象。从语法上讲,它与普通函数行为类似。
2、greater<>与less<>就是函数对象。
3、容器就是调用函数对象的operator()方法去比较两个值的大小
*/
struct FuncStudent
{
//重载()
bool operator()(const Student &left, const Student &right)
{
//如果左边的小 就返回真 从小到大按照年龄进行排序
if (left.age < right.age)
{
return true;
}
else
{
return false;
}
}
};
/*
注意 函数运行结果显示s5插入失败,但没有提示
*/
void test03()
{
Student s1("s1", 31);
Student s2("s2", 22);
Student s3("s3", 44);
Student s4("s4", 11);
Student s5("s5", 31);//如果两个31岁 能插入成功
//如何知道 插入 的结果
//实际上 s5 插入失败
//调用访函数
set<Student, FuncStudent> set1;
set1.insert(s1);//打断点追踪 查看函数的返回值
set1.insert(s2);
set1.insert(s3);
set1.insert(s4);
set1.insert(s5);
//注意set<Student, FuncStudent>
for (set<Student, FuncStudent>::iterator it = set1.begin(); it != set1.end(); it++)
{
cout << it->age << "\t" << it->name << endl;
}
}
/*
pair译为对组,可以将两个值视为一个单元。
pair<T1,T2>存放的两个值的类型,可以不一样,如T1为int,T2为float。T1,T2也可以是自定义类型。
pair.first是pair里面的第一个值,是T1类型。
pair.second是pair里面的第二个值,是T2类型
*/
/*
1、如何判断 set1.insert函数的返回值?
2、Pair的用法:对组,一个整体的单元,存放两个类型(T1,T2,T1可与T2一样)的两个元素。
3、单步调试追踪函数返回值 typedef pair<iterator, bool> _Pairib;
*/
void test04()
{
Student s1("s1", 31);
Student s2("s2", 22);
Student s3("s3", 44);
Student s4("s4", 11);
Student s5("s5", 31);//如果两个31岁 能插入成功
//如何知道 插入 的结果
set<Student, FuncStudent> set1;
//指定什么类型的迭代器 set<Student, FuncStudent>::iterator
pair<set<Student, FuncStudent>::iterator, bool> pair1 = set1.insert(s1);
if (pair1.second == true)
{
cout << "插入s1成功" << endl;
}
else
{
cout << "插入s1失败" << endl;
}
set1.insert(s2);
pair<set<Student, FuncStudent>::iterator, bool> pair2 = set1.insert(s5);
if (pair2.second == true)
{
cout << "插入s5成功" << endl;
}
else
{
cout << "插入s5失败" << endl;
}
}
/*
find查找 equal_range 返回结果是一个pair
set.find(elem); //查找elem元素,返回指向elem元素的迭代器。
set.count(elem); //返回容器中值为elem的元素个数。对set来说,要么是0,要么是1。对multiset来说,值可能大于1。
set.lower_bound(elem); //返回第一个 >=elem 元素的迭代器。
set.upper_bound(elem); //返回第一个 >elem 元素的迭代器。
set.equal_range(elem); //返回容器中与elem相等的上下限的两个迭代器。上限是闭区间,下限是开区间,如[beg,end)。
以上函数返回两个迭代器,而这两个迭代器被封装在pair中
*/
void test05()
{
set<int> set1;
for (int i = 0; i < 10; i++)
{
set1.insert(i + 1);
}
//遍历
for (set<int>::iterator it = set1.begin(); it != set1.end(); it++)
{
cout << *it << " ";
}
cout << endl;
set<int>::iterator it0 = set1.find(5);
cout << "it0: " << *it0 << endl;
int num1 = set1.count(5);
cout << "num1: " << num1 << endl;
set<int>::iterator it1 = set1.lower_bound(5);//返回大于等于5的迭代器的位置
cout << "*it1: " << *it1 << endl;
set<int>::iterator it2 = set1.upper_bound(5);//返回大于5的迭代器的位置
cout << "*it2: " << *it2 << endl;
//把5 元素删除掉
set1.erase(5);//删除容器中值为5 的元素
/*
typedef pair<iterator, bool> _Pairib;
typedef pair<iterator, iterator> _Pairii;
typedef pair<const_iterator, const_iterator> _Paircc;
*/
pair<set<int>::iterator, set<int>::iterator> mypair = set1.equal_range(5);
set<int>::iterator it3 = mypair.first; //第一个迭代器找大于等于5的
cout << "it3:" << *it3 << endl; //5 //6
set<int>::iterator it4 = mypair.second; //第二个迭代器找大于5的
cout << "it4:" << *it4 << endl; //6 //6
}
/*
multiset的用法
*/
void test06()
{
multiset<int> set1;
int tmp = 0;
printf("请输入multiset集合的值:");
scanf("%d", &tmp);
while (tmp != 0)
{
set1.insert(tmp);
printf("请输入multiset集合的值:");
scanf("%d", &tmp);
}
for (multiset<int>::iterator it = set1.begin(); it != set1.end(); it++)
{
cout << *it << " ";
}
cout << endl;
while (!set1.empty())
{
multiset<int>::iterator it = set1.begin();
cout << *it << " ";
set1.erase(it);
}
}
void test()
{
//test01();
//test02();
test03();
//test04();
//test05();
//test06();
}
#endif
#include <map>
/*
map的使用
1、map是标准的关联式容器,一个map是一个键值对序列,即(key,value)对。
它提供基于key的快速检索能力。
2、map中key值是唯一的。集合中的元素按一定的顺序排列。元素插入过程
是按排序规则插入,所以不能指定插入位置。
3、map的具体实现采用红黑树变体的平衡二叉树的数据结构。在插入操作和
删除操作上比vector快。
4、map可以直接存取key所对应的value,支持[]操作符,如map[key]=value
5、multimap与map的区别:map支持唯一键值,每个键只能出现一次;而multimap中相同键可以出现多次。multimap不支持[]操作符。
6、#include <map>
*/
#if 0
/*
map元素的添加/遍历/删除基本操作
map.insert(...); //往容器插入元素,返回pair<iterator,bool>
*/
void test01()
{
map<int, string> map1;//key-value
//方法1 通过 pair 的方式插入对象
map1.insert(pair<int, string>(1, "teacher01"));
map1.insert(pair<int, string>(2, "teacher02"));
//方法2 通过 make_pair 的方式插入对象 最方便
map1.insert(make_pair(3, "teacher03"));
map1.insert(make_pair(4, "teacher04"));
//方法3 通过 value_type 的方式插入对象
map1.insert(map<int, string>::value_type(5, "teacher05"));
map1.insert(map<int, string>::value_type(6, "teacher06"));
//方法4 通过数组的方式插入值 不建议使用
map1[7] = "teacher07";//重载了[] =
map1[8] = "teacher08";
//容器的遍历
for (map<int, string>::iterator it = map1.begin(); it != map1.end(); it++)
{
cout << it->first << "\t" << it->second << endl;
}
cout << "遍历结束" << endl;
//容器元素的删除
while (!map1.empty())
{
map<int, string>::iterator it = map1.begin();
cout << it->first << "\t" << it->second << endl;
map1.erase(it);//迭代器会自动下移
}
}
/*
插入的四种方法 异同
前三种方法 返回值为pair<iterator,bool> 则,若key已经存在,则报错
方法四 则,若key已经存在,则修改
*/
void test02()
{
map<int, string> map1;
//typedef pair<iterator, bool> _Pairib; _Pairib
//方法1 注意如何查找函数的返回值
pair<map<int, string>::iterator, bool> mypair1 = map1.insert(pair<int, string>(1, "teacher01"));
//方法2
pair<map<int, string>::iterator, bool> mypair2 = map1.insert(make_pair(5, "teacher05"));
if (mypair2.second != true)
{
cout << "key05插入失败" << endl;
}
else
{
//注意如何使用
cout << mypair2.first->first << "\t" << mypair2.first->second << endl;
}
//方法3
pair<map<int, string>::iterator, bool> mypair3 = map1.insert(map<int, string>::value_type(5, "teacher55"));
if (mypair3.second != true)
{
cout << "key05插入失败" << endl;
}
else
{
//注意如何使用
cout << mypair3.first->first << "\t" << mypair3.first->second << endl;
}
map1.insert(map<int, string>::value_type(6, "teacher06"));
//方法4 使用方法4,若key已经存在,则修改
map1[7] = "teacher07";
map1[7] = "teacher77";
//容器的遍历
for (map<int, string>::iterator it = map1.begin(); it != map1.end(); it++)
{
cout << it->first << "\t" << it->second << endl;
}
cout << "遍历结束" << endl;
//容器元素的删除
while (!map1.empty())
{
map<int, string>::iterator it = map1.begin();
cout << it->first << "\t" << it->second << endl;
map1.erase(it);
}
}
/*
find 使用
*/
void test03()
{
map<int, string> map1;
//方法1
map1.insert(pair<int, string>(1, "teacher01"));
map1.insert(pair<int, string>(2, "teacher02"));
//方法2
map1.insert(make_pair(3, "teacher03"));
map1.insert(make_pair(4, "teacher04"));
//方法3
map1.insert(map<int, string>::value_type(5, "teacher05"));
map1.insert(map<int, string>::value_type(6, "teacher06"));
//方法4
map1[7] = "teacher07";
map1[8] = "teacher08";
//容器的遍历
for (map<int, string>::iterator it = map1.begin(); it != map1.end(); it++)
{
cout << it->first << "\t" << it->second << endl;
}
cout << "遍历结束" << endl;
//map 查找key为100 的元素 异常处理 find 函数返回一个迭代器
map<int, string>::iterator it2 = map1.find(100);
if (it2 == map1.end())
{
cout << "key 100 的值不存在!!!" << endl;
}
else
{
cout << it2->first << "\t" << it2->second << endl;
}
//equal_range 的异常处理
pair<map<int,string>::iterator, map<int, string>::iterator> mypair1 = map1.equal_range(5);
//第一个迭代器 >= 5 的位置
if (mypair1.first == map1.end())
{
cout << "第一个迭代器>=5 的位置 不存在" << endl;
}
else
{
cout << mypair1.first->first << "\t" << mypair1.first->second << endl;
}
//第二个迭代器 > 5 的位置
if (mypair1.second == map1.end())
{
cout << "第二个迭代器 > 5 的位置 不存在" << endl;
}
else
{
cout << mypair1.second->first << "\t" << mypair1.second->second << endl;
}
}
/*
multimap
1个key值可以对应多个valude =分组技术 用于sql语句
公司有销售部 sale (员工2名)、技术研发部 development (1人)、
财务部 Financial (2人)
//人员信息有:姓名,年龄,电话、工资等组成
//通过 multimap进行 信息的插入、保存、显示
//分部门显示员工信息
*/
class Person
{
public:
string name;
int age;
string tel;
double saly;
};
void test04()
{
Person p1, p2, p3, p4, p5;
p1.name = "王1";
p1.age = 31;
p2.name = "王2";
p2.age = 32;
p3.name = "张3";
p3.age = 33;
p4.name = "张4";
p4.age = 34;
p5.name = "赵5";
p5.age = 35;
multimap<string, Person> map2;//key-->多value
//sale 部门
map2.insert(make_pair("sale", p1));
map2.insert(make_pair("sale", p2));
//development 部门
map2.insert(make_pair("development", p3));
map2.insert(make_pair("development", p4));
//Financial 部门
map2.insert(make_pair("Financial", p5));
//遍历
for (multimap<string, Person>::iterator it = map2.begin(); it != map2.end(); it++)
{
cout << it->first << "\t" << it->second.name << "\t" << it->second.age << endl;
}
//统计 development 部门的人数 使用count算法
int num2 = map2.count("development");
cout << "development 部门人数:" << num2 << endl;
cout << "------development 部门员工信息------"<< endl;
//查找key
multimap<string, Person>::iterator it2 = map2.find("development");
int tag = 0;
while (it2 != map2.end() && tag < num2)
{
cout << it2->first << "\t" << it2->second.name << endl;
it2++;
tag++;
}
//age = 32的名字修改成 name32
cout << endl << "按照条件 检索数据 进行修改" << endl;
//遍历
for (multimap<string, Person>::iterator it = map2.begin(); it != map2.end(); it++)
{
if (it->second.age == 32)
{
it->second.name = "name32";
}
}
//遍历
for (multimap<string, Person>::iterator it = map2.begin(); it != map2.end(); it++)
{
cout << it->first << "\t" << it->second.name << "\t" << it->second.age << endl;
}
}
void test()
{
//test01();
//test02();
//test03();
test04();
}
#endif
/*
容器共性机制研究
1、c++模板是容器的概念
1)容器中缓存了用户的结点
2)结点的类,要保证结点能够插入到容器中
一般结点类,需要提供
(1)无参构造函数 (2)拷贝构造函数 (3)重载=操作符
2、理论提高:所有容器提供的都是值(value)语意,而非引用(reference)语意。
容器执行插入元素的操作时,内部实施拷贝动作。所以STL容器内存储的元素必须
能够 被拷贝(必须提供拷贝构造函数)。
*/
#if 0
/*
容器值(value)语意
*/
class Teacher
{
public:
Teacher(char * name, int age)
{
m_pname = new char[strlen(name) + 1];
strcpy(m_pname, name);
m_age = age;
}
~Teacher()
{
if (m_pname != NULL)
{
delete[] m_pname;
m_pname = NULL;
m_age = 0;
}
}
//Teacher t2 = t1
Teacher(const Teacher &obj)
{
//分配新的内存
m_pname = new char[strlen(obj.m_pname) + 1];
//拷贝数据
strcpy(m_pname, obj.m_pname);
m_age = obj.m_age;
}
//重载= 号操作符
//t3 = t2 = t1
Teacher & operator=(const Teacher& obj)
{
//1释放旧的内存
if (this->m_pname != NULL)
{
delete[]m_pname;
m_pname = NULL;
m_age = 0;
}
//2重新分配内存
this->m_pname = new char[strlen(obj.m_pname) + 1];
//数据拷贝
strcpy(m_pname, obj.m_pname);
m_age = obj.m_age;
//返回*this
return *this;
}
public:
void printT()
{
cout << m_pname << "\t" << m_age << endl;
}
protected:
private:
char *m_pname;
int m_age;
};
void test01()
{
Teacher t1("t1", 31);
t1.printT();
vector<Teacher> v1;
v1.push_back(t1);//把t1拷贝了一份 存入到容器中了....
}
void test()
{
test01();
}
#endif
/*
函数对象:
重载函数调用操作符()的类,其对象常称为函数对象(function object),即它们是
行为类似函数的对象。一个类对象,表现出一个函数的特征,就是通过
“对象名+(参数列表)”的方式使用一个类对象,如果没有上下文,完全可以把它
看作一个函数对待。
这是通过重载类的operator()来实现的
*/
#include<functional>
#if 0
//函数对象 类重载了()
template <typename T>
class ShowElemt
{
public:
ShowElemt()
{
n = 0;
}
//重载()
void operator()(T &t)
{
n++;
cout << t << "--";
}
void printN()
{
cout << "n:" << n << endl;
}
protected:
private:
int n;
};
//函数模板 ==函数
template <typename T>
void FuncShowElemt(T &t)
{
cout << t << endl;
}
//普通函数
void FuncShowElemt2(int &t)
{
cout << t << " ";
}
//函数对象的定义:函数对象和普通函数的异同
void test01()
{
int a = 10;
//定义一个对象
ShowElemt<int> ShowElemt0;
ShowElemt0(a);//函数对象的()的执行,很像一个函数
FuncShowElemt<int>(a); //构造函数
FuncShowElemt2(a); //普通函数执行
}
/*
函数对象是属于类对象,能突破函数的概念,能保持调用信息
函数对象的好处
for_each 算法中,函数对象做函数参数
for_each 算法中,函数对象当返回值
*/
void test02()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
v1.push_back(7);
//for_each用指定函数依次对指定范围内所有元素进行迭代访问,返回所指定的函数类型。
//该函数不得修改序列中的元素
//调用函数对象
for_each(v1.begin(), v1.end(), ShowElemt<int>());//ShowElemt<int>()匿名函数对象,匿名访函数
cout << endl;
//调用普通函数
for_each(v1.begin(), v1.end(), FuncShowElemt2);//通过回调函数 谁使用for_each 谁去填写回调函数的入口地址
ShowElemt<int> show1;//定义一个有名对象
/*
函数: 函数名 函数参数 返回值
template<class _InIt, class _Fn1>
inline _Fn1 for_each(_InIt _First, _InIt _Last, _Fn1 _Func)
{
// perform function for each element
_DEBUG_RANGE(_First, _Last);
_DEBUG_POINTER(_Func);
_For_each(_Unchecked(_First), _Unchecked(_Last), _Func);
return (_STD move(_Func));
}
*/
//for_each 算法的 函数对象的传递 是元素值的传递,不是引用传递
//用实参show1初始化形参show1,会调用这个对象的类的copy构造函数
//实参和形参是2个不同的概念
//形参的值可以通过for_each的返回值传递出来
for_each(v1.begin(), v1.end(), show1);
show1.printN();
cout << "通过for_each算法的返回值看调用的次数" << endl;
show1 = for_each(v1.begin(), v1.end(), show1); //通过函数返回值,返回函数的调用状态信息
show1.printN();
/*
结论要点:
分清楚 stl算法返回值是迭代器 还是谓词(函数对象)
是stl算法入门的重要点
*/
}
/*
一元谓词:
函数参数1个,函数返回值是bool类型,可以作为一个判断式
谓词可以是一个仿函数(函数对象),也可以是一个回调函数。
*/
//template 告诉编译器要进行泛型编程
//typename T 告诉编译器看到T不要报错
template<typename T>
class IsDiv
{
public:
IsDiv(const T &divisor)
{
this->divisor = divisor;
}
//符合一元谓词的条件
bool operator()(T &t)
{
return (t%divisor == 0);
}
protected:
private:
T divisor;
};
void test03()
{
vector<int> v2;
for (int i = 10; i < 33; i++)
{
v2.push_back(i);
}
int a = 4;
IsDiv<int> mydiv(a);
//使用输入的函数代替等于操作符执行find
find_if(v2.begin(), v2.end(), mydiv);//法1
/*
函数的返回值是一个迭代器 注意参数的类型 和返回值的类型
template<class _InIt,class _Pr>
inline _InIt find_if(_InIt _First, _InIt _Last, _Pr _Pred)
{
// find first satisfying _Pred
_DEBUG_RANGE(_First, _Last);
_DEBUG_POINTER(_Pred);
return (_Rechecked(_First,
_Find_if(_Unchecked(_First), _Unchecked(_Last), _Pred)));
}
*/
vector<int>::iterator it;
it = find_if(v2.begin(), v2.end(), IsDiv<int>(4));//法2
if (it == v2.end())
{
cout << "容器中没有被4整除的元素" << endl;
}
else
{
cout << "第一个被4整除的元素是:" << *it << endl;
}
}
//二元函数对象
template<typename T>
class sumAdd
{
public:
T operator()(T t1, T t2)
{
return t1 + t2;
}
};
int mysum(int &t1, int &t2)
{
return t1 + t2;
}
//二元函数对象
void test04()
{
//v1 v2 ==> v3
vector<int> v1, v2;
vector<int> v3;
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
v2.push_back(2);
v2.push_back(4);
v2.push_back(6);
v3.resize(10);
/*
template<class _InIt1,
class _InIt2,
class _OutIt,
class _Fn2> inline
_OutIt transform(_InIt1 _First1, _InIt1 _Last1,
_InIt2 _First2, _OutIt _Dest, _Fn2 _Func)
{ // transform [_First1, _Last1) and [_First2, ...) with _Func
_DEBUG_RANGE(_First1, _Last1);
_DEBUG_POINTER(_Dest);
_DEBUG_POINTER(_Func);
if (_First1 != _Last1)
return (_Transform2(_Unchecked(_First1), _Unchecked(_Last1),
_First2, _Dest, _Func,
_Is_checked(_Dest)));
return (_Dest);
}
*/
//transform 把运算结果的 迭代器的开始位置返回出来
transform(v1.begin(), v1.end(), v2.begin(), v3.begin(), sumAdd<int>());
//transform(v1.begin(), v1.end(), v2.begin(), v3.begin(), mysum )
for (vector<int>::iterator it = v3.begin(); it != v3.end(); it++)
{
cout << *it << " ";
}
}
//二元谓词 函数参数2个,函数返回值是bool类型 可以作为一个判断式
//谓词可以使一个仿函数,也可以是一个回调函数
bool MyCompare(const int &a, const int &b)
{
return a < b;//从小到大
}
void test05()
{
vector<int> v1(10);
for (int i = 0; i < 10; i++)
{
int tmp = rand() % 100;
v1[i] = tmp;
}
//遍历 方法1
for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
{
cout << *it << " ";
}
cout << endl;
//方法2 使用函数对象
for_each(v1.begin(), v1.end(), FuncShowElemt2);
cout << endl;
//从小到大排序 使用二元谓词
sort(v1.begin(), v1.end(), MyCompare);
for_each(v1.begin(), v1.end(), FuncShowElemt2);
cout << endl;
}
//二元谓词在set集合中的应用
struct CompareNoCase //也可以用类
{
bool operator()(const string &str1, const string &str2)
{
string str1_;
str1_.resize(str1.size());
transform(str1.begin(), str1.end(), str1_.begin(), tolower);//预定义函数对象
string str2_;
str2_.resize(str2.size());
transform(str2.begin(), str2.end(), str2_.begin(), tolower);//预定义函数对象
return (str1_ < str2_);
}
};
//二元谓词在set集合中的应用
void test06()
{
set<string> set1;
set1.insert("bbb");
set1.insert("aaa");
set1.insert("ccc");
set<string>::iterator it = set1.find("aAa");//find函数 默认 区分大小写
if (it == set1.end())
{
cout << "没有查找到 aAa" << endl;
}
else
{
cout << "查找到 aAa" << endl;
}
set<string, CompareNoCase> set2;
set2.insert("bbb");
set2.insert("aaa");
set2.insert("ccc");
set<string, CompareNoCase>::iterator it2 = set2.find("aAa");
if (it2 == set2.end())
{
cout << "没有查找到 aaa" << endl;
}
else
{
cout << "不区分大小的查找 查找到 aAa" << endl;
}
}
void test()
{
//test01(); //函数对象基本概念
test02(); //函数对象的好处 函数对象做函数参数 函数对象做返回值
//test03(); //一元谓词
//test04(); //二元函数对象 和二元谓词
//test05(); //二元函数对象 和二元谓词
//test06(); //二元谓词在set集合中的应用
}
#endif
/*
预定义函数对象:
1)算数函数对象
加法:plus<Types>
减法:minus<Types>
乘法:multiplies<Types>
除法divides<Tpye>
求余:modulus<Tpye>
取反:negate<Type>
2)关系函数对象
等于equal_to<Tpye>
equal_to<string> stringEqual;
sres = stringEqual(sval1,sval2);
不等于not_equal_to<Type>
大于 greater<Type>
大于等于greater_equal<Type>
小于 less<Type>
小于等于less_equal<Type>
3)逻辑函数对象
逻辑与 logical_and<Type>
logical_and<int> indAnd;
ires = intAnd(ival1,ival2);
dres=BinaryFunc( logical_and<double>(),dval1,dval2);
逻辑或logical_or<Type>
逻辑非logical_not<Type>
logical_not<int> IntNot;
Ires = IntNot(ival1);
Dres=UnaryFunc( logical_not<double>,dval1);
函数适配器:实现将一种函数对象转化为另一种符合要求的函数对象
plus<int> 预定义好的函数对象 能实现不同类型的数据的 +运算
实现了 数据类型和算法的分离==>通过函数对象技术实现的
思考 怎么样知道 plus<type> 是两个参数 查看源码
*/
#if 0
void test01()
{
/*
查看plus的源码
template<class _Ty = void>
struct plus
: public binary_function<_Ty, _Ty, _Ty>
{ // functor for operator+
_Ty operator()(const _Ty& _Left, const _Ty& _Right) const
{ // apply operator+ to operands
return (_Left + _Right);
}
};
*/
plus<int> intAdd;//预定义好的函数对象
int x = 10;
int y = 10;
int z = intAdd(x, y);
cout << "z: " << z << endl;
plus<string> stringAdd;
string s1 = "aaa";
string s2 = "bbb";
string s3 = stringAdd(s1,s2);
cout << "s3: " << s3 << endl;
vector<string> v1;
v1.push_back("bbb");
v1.push_back("aaa");
v1.push_back("ccc");
v1.push_back("zzz");
v1.push_back("ccc");
v1.push_back("ccc");
/*
template<class _Ty = void>
struct greater
: public binary_function<_Ty, _Ty, bool>
{ // functor for operator>
bool operator()(const _Ty& _Left, const _Ty& _Right) const
{ // apply operator> to operands
return (_Left > _Right);
}
};
*/
sort(v1.begin(), v1.end(), greater<string>());//从大到小
for (vector<string>::iterator it = v1.begin(); it != v1.end(); it++)
{
cout << *it << endl;
}
//求ccc出现的个数
string sc = "ccc";
//equal_to<string>()有两个参数 left参数来自容器 right参数来自sc
//bind2end 函数适配器:把预定义函数对象 和第二个参数进行绑定,
//适配成一元函数bind2nd(op, value)
//将equal_to<string>()和 sc变为一个参数
int num = count_if(v1.begin(), v1.end(), bind2nd(equal_to<string>(), sc));
cout << "num : " << num << endl;
}
class IsGreat
{
public:
IsGreat(int i)
{
m_num = i;
}
//一元谓词
bool operator()(int &num)//num来自vector中的元素
{
//cout << "num" << "--" << num << "---";
if (num > m_num)
{
return true;
}
return false;
}
private:
int m_num;
};
void test02()
{
vector<int> v1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i + 1);
}
for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
{
cout << *it << " ";
}
cout << endl;
int num1 = count(v1.begin(), v1.end(), 3);
cout << " num1: " << num1 << endl;
//通过 谓词 求大于2 的个数
int num2 = count_if(v1.begin(), v1.end(), IsGreat(2));
cout << "num2: " << num2 << endl;
//通过 预定义函数对象 求大于2 的个数
//greater<int>()有两个参数 左参数来自容器的元素,右参数固定成2(通过bind2end做的)
int num3 = count_if(v1.begin(), v1.end(), bind2nd(greater<int>(), 2));
cout << "num3:" << num3 << endl;
//求奇数的个数 modulus<type>求余
int num4 = count_if(v1.begin(), v1.end(), bind2nd(modulus<int>(), 2));
cout << "奇数的个数num4:" << num4 << endl;
/*
取反器(negator) :
negator是一个将函数对象的值翻转的函数适配器。标准库提供两个预定义
的ngeator适配器:not1翻转一元预定义函数对象的真值,而not2翻转二元
谓词函数的真值。
*/
//求偶数的个数 negator取反器
int num5 = count_if(v1.begin(), v1.end(), not1(bind2nd(modulus<int>(), 2)));
cout << "偶数的个数 num5:" << num5 << endl;
}
void test()
{
//test01();
test02();
}
#endif
/*
STL的容器算法迭代器的设计理念
容器统一性:基于类模板技术
实现了自定义数据类型和标准数据类型的运算
实现了数据类型和容器模型的分离
算法统一性:基于函数对象(本质:回调函数,将任务的编写者与任务的调用者
进行有效的分离)
迭代器将容器和算法整合在一起
*/
/*
算法for_each 的使用 属于修改型算法
*/
#if 0
void printV(vector<int> &v)
{
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << endl;
}
}
void showElem(int &n)
{
cout << n << " ";
}
class CMyshow
{
public:
CMyshow()
{
num = 0;
}
//重载函数操作符
void operator()(int &n)
{
num++;
cout <<"-" << n << "-";
}
void printNum()
{
cout << "num:" << num << endl;
}
protected:
private:
int num;
};
void test01()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
printV(v1);
cout << endl;
/*
template<class _InIt,
class _Fn1> inline
_Fn1 for_each(_InIt _First, _InIt _Last, _Fn1 _Func)
{ // perform function for each element
_DEBUG_RANGE(_First, _Last);
_DEBUG_POINTER(_Func);
_For_each(_Unchecked(_First), _Unchecked(_Last), _Func);
return (_STD move(_Func));
}
*/
//函数对象 回调函数的入地址
for_each(v1.begin(), v1.end(), showElem);
cout << endl;
//使用匿名对象CMyshow() 实参初始化形参 会调用形参的copy构造函数
for_each(v1.begin(), v1.end(), CMyshow());
cout << endl;
CMyshow mya;//用mya实参初始化形参
//for_each()函数返回一个匿名对象,用来初始化my1 匿名对象被转正 不会被析构
CMyshow my1 = for_each(v1.begin(), v1.end(), mya);
cout << endl;
mya.printNum();//mya与my1是两个不同的对象
my1.printNum();
cout << endl;
my1 = for_each(v1.begin(), v1.end(), mya); //给my1赋值 匿名对象会被析构
cout << endl;
my1.printNum();
}
/*
transform的使用
*/
//transform所使用的函数对象,参数一般不使用引用,而是还有返回值
int increase(int i)
{
return i + 100;
}
void printList(list<int> &mylist)
{
for (list<int>::iterator it = mylist.begin(); it != mylist.end(); it++)
{
cout << *it << endl;
}
}
void test02()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
printV(v1);
cout << endl;
//transform使用回调函数
transform(v1.begin(), v1.end(), v1.begin(), increase);
printV(v1);
cout << endl;
//transform使用预定义的函数对象
transform(v1.begin(), v1.end(), v1.begin(), negate<int>());
printV(v1);
cout << endl;
//transform 使用 函数适配器 和函数对象
list<int> mylist;
mylist.resize(v1.size());
//转换的结果放在链表中
transform(v1.begin(), v1.end(), mylist.begin(), bind2nd(multiplies<int>(), 10));
printList(mylist);
cout << endl;
//transform 也可以把运算结果 直接输出到屏幕
transform(v1.begin(), v1.end(), ostream_iterator<int>(cout, " "), negate<int>());
cout << endl;
}
/*
foreach与transform 的异同
一般情况下:for_each所使用的函数对象,参数是引用,没有返回值
transform所使用的函数对象,参数一般不使用引用,而是还有返回值
*/
void myshowElem(int &n)
{
cout << n << " ";
}
int myshowElem2(int n)
{
cout << n << " ";
return n;
}
void test03()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
vector<int> v2 = v1;
for_each(v1.begin(), v1.end(), myshowElem);
cout << endl;
transform(v2.begin(), v2.end(), v2.begin(), myshowElem2);
/*
template<class _InIt,
class _OutIt,
class _Fn1> inline
_OutIt transform(_InIt _First, _InIt _Last,
_OutIt _Dest, _Fn1 _Func)
{ // transform [_First, _Last) with _Func
_DEBUG_RANGE(_First, _Last);
_DEBUG_POINTER(_Dest);
_DEBUG_POINTER(_Func);
if (_First != _Last)
return (_Transform1(_Unchecked(_First), _Unchecked(_Last),
_Dest, _Func, _Is_checked(_Dest)));
return (_Dest);
}
template<class _InIt,
class _OutIt,
class _Fn1> inline
_OutIt _Transform(_InIt _First, _InIt _Last,
_OutIt _Dest, _Fn1 _Func)
{ // transform [_First, _Last) with _Func
for (; _First != _Last; ++_First, ++_Dest)
*_Dest = _Func(*_First);//解释了为什么要有返回值
return (_Dest);
}
*/
}
/*
adjacent_find在iterator对标识元素范围内,查找一对相邻重复元素,
找到则返回指向这对元素的第一个元素的迭代器。否则返回past-the-end
*/
void test04()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(2);
v1.push_back(3);
v1.push_back(5);
vector<int>::iterator it = adjacent_find(v1.begin(), v1.end());
if (it == v1.end())
{
cout << "没有找到重复的元素" << endl;
}
else
{
cout << *it << endl;
}
int index = distance(v1.begin(), it);//计算迭代器与元素开始的位置
cout << "重复元素的下标为: " << index << endl;
}
/*
binary_search二分法查找
1000 个数使用二分法查找 1024 = 2^10次方 查10次即可 速度快
在有序序列中查找value,找到则返回true。注意:在无序序列中,不可使用
*/
void test05()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
v1.push_back(7);
v1.push_back(9);
bool b = binary_search(v1.begin(), v1.end(), 5);
if (b == true)
{
cout << "找到了" << endl;
}
else
{
cout << "没到了" << endl;
}
}
/*
count利用等于操作符,把标志范围内的元素与输入值比较,返回相等的个数
*/
void test06()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
v1.push_back(7);
v1.push_back(7);
v1.push_back(9);
v1.push_back(7);
int num = count(v1.begin(), v1.end(), 7);
cout << num << endl;
}
/*
使用自定义类型查找
count_if 根据条件进行查找
*/
//一元谓词
bool GreatThree(int inum)
{
if (inum > 3)
{
return true;
}
return false;
}
void test07()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
v1.push_back(7);
v1.push_back(7);
v1.push_back(9);
v1.push_back(7);
int num = count_if(v1.begin(), v1.end(), GreatThree);
cout << "num:" << num << endl;
}
/*
find 和find_if
*/
void test08()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
v1.push_back(7);
v1.push_back(7);
v1.push_back(9);
v1.push_back(7);
//查找值为5 的元素的位置
vector<int>::iterator it = find(v1.begin(), v1.end(), 5);
cout << *it << endl;
//返回第一个大于3 的位置
vector<int>::iterator it2 = find_if(v1.begin(), v1.end(), GreatThree);
cout << *it2 << endl;
}
/*
merge:合并两个有序序列,存放到另一个序列
*/
void test09()
{
vector<int> v1;//有序序列
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
vector<int> v2;//有序序列
v2.push_back(2);
v2.push_back(4);
v2.push_back(6);
vector<int> v3;
//注意扩大容量
v3.resize(v1.size() + v2.size());
merge(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin());
printV(v3);
}
/*
sort:以默认升序的方式重新排列指定范围内的元素。
若要改排序规则,可以输入比较函数
*/
class Student
{
public:
Student(string name, int id)
{
m_name = name;
m_id = id;
}
void printT()
{
cout << "name: " << m_name << "id: " << m_id << endl;
}
public:
string m_name;
int m_id;
};
//二元谓词
bool CompareS(Student &s0, Student &s1)
{
return (s0.m_id < s1.m_id);
}
void test10()
{
Student s1("老大", 1);
Student s2("老二", 2);
Student s3("老三", 3);
Student s4("老四", 4);
vector<Student> v1;
v1.push_back(s4);
v1.push_back(s1);
v1.push_back(s3);
v1.push_back(s2);
for (vector<Student>::iterator it = v1.begin(); it != v1.end(); it++)
{
it->printT();
}
/*
根据自定义函数对象 进行自定义数据类型的排序
替换 算法的统一性(实现算法和数据类型的分离)==》技术手段函数对象
*/
sort(v1.begin(), v1.end(),CompareS);
for (vector<Student>::iterator it = v1.begin(); it != v1.end(); it++)
{
it->printT();
}
}
/*
random_shuffle:对指定范围内的元素随机调整次序
srand(time(0));设置随机种子
*/
void test11()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
v1.push_back(7);
random_shuffle(v1.begin(), v1.end());
printV(v1);
string str = "abcdefg";
random_shuffle(str.begin(), str.end());
cout << "str: " << str << endl;
}
/*
reverse将所有元素取反
*/
void test12()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
v1.push_back(7);
reverse(v1.begin(), v1.end());
printV(v1);
}
/*
常用的拷贝和替换算法
copy拷贝算法
*/
void test13()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
v1.push_back(7);
vector<int> v2;
v2.resize(v1.size());
copy(v1.begin(), v1.end(), v2.begin());
printV(v2);
}
/*
replace和replace_if
将指定范围内的所有等于oldValue的元素替换成newValue
replace(beg,end,oldValue,newValue)
replace_if : 将指定范围内所有操作结果为true的元素用新值替换
*/
bool great_equal_5(int &obj)
{
if (obj > 5)
{
return true;
}
return false;
}
void test14()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
v1.push_back(7);
v1.push_back(3);
replace(v1.begin(), v1.end(), 3, 8);
printV(v1);
//将容器中大于5的值用 1 替换
replace_if(v1.begin(), v1.end(), great_equal_5, 1);
printV(v1);
}
/*
swap:交换两个容器的元素
*/
void test15()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
vector<int> v2;
v2.push_back(2);
v2.push_back(4);
v2.push_back(6);
swap(v1, v2);
printV(v1);
}
/*
常用的算术和生成算法
accumulate
对指定范围内的元素求和,然后结果再加上一个由val指定的初始值。
fill
将输入值赋给标志范围内的所有元素
*/
#include<numeric>
void test16()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
int tmp = accumulate(v1.begin(), v1.end(), 100);
cout << "tmp: " << tmp << endl;
fill(v1.begin(), v1.end(), 5);
printV(v1);
}
/*
常用的集合算法
set_union:
构造一个有序序列,包含两个有序序列的并集。
set_intersection:
构造一个有序序列,包含两个有序序列的交集。
set_difference:
构造一个有序序列,该序列保留第一个有序序列中存在
而第二个有序序列中不存在的元素
*/
void test17()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(3);
v1.push_back(5);
vector<int> v2;
v2.push_back(3);
v2.push_back(4);
v2.push_back(6);
vector<int> v3;
v3.resize(v1.size() + v2.size());
//并集
set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin());
printV(v3);
cout << "-----" << endl;
vector<int> v4;
v4.resize(v1.size() + v2.size());
//交集
set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), v4.begin());
printV(v4);
cout << "-----" << endl;
vector<int> v5;
v5.resize(v1.size() + v2.size());
//差集
set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), v5.begin());
printV(v5);
}
void test()
{
//test01();
//test02();
//test03(); //foreach与transform 的异同
//test04(); //adjacent_find
//test05(); //binary_search
//test06();//count
//test07();//count_if
//test08();//find和find_if
//test09();//merge
//test10();//sort
//test11();//random_shuffle
//test12();//reverse
//test13();//copy
//test14();//replace和replace_if
//test15();//swap
//test16();//accumulate 和fill
test17();
}
#endif
/*
stl 演讲比赛
*/
#include <numeric>
#if 0
class Speaker
{
public:
string m_name; //选手名
int m_score[3]; //24个人 分为4 组 选手得分
};
//产生选手
int GenSpeaker(map<int, Speaker> &mapSpeaker, vector<int> &v)
{
string str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
random_shuffle(str.begin(), str.end()); //对指定范围内的元素随机调整次序
for (int i = 0; i < 24; i++)
{
Speaker tmp;
tmp.m_name = "选手";
tmp.m_name = tmp.m_name + str[i];
//选手编号 选手 key-value
mapSpeaker.insert(pair<int, Speaker>(100 + i, tmp));
}
//将参加比赛的人员编号放入容器中
for (int i = 0; i < 24; i++)
{
v.push_back(100 + i);
}
return 0;
}
//选手抽签
int speech_contest_draw(vector<int> &v)
{
random_shuffle(v.begin(), v.end());
return 0;
}
//选手比赛
int speech_contest(int index, vector<int> &v1, map<int, Speaker> &mapSpeaker, vector<int> &v2)
{
//小组比赛得分 记录下来 求出前3名 后3名
multimap<int, int, greater<int>> multimapGroup;//小组成绩
int tmpCount = 0;
for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
{
tmpCount++;
//打分 10个评委为每一个选手打分 去掉最高分 和最低分 求剩余平均分
{
//选择双端队列
deque<int> dscore;
for (int j = 0; j < 10; j++)//10个评委打分
{
int score = 50 + rand() % 50;
dscore.push_back(score);
}
//排序
sort(dscore.begin(), dscore.end());//默认是升序
dscore.pop_back();//去除最高分
dscore.pop_front();//去除最低分
//求平均分 则为这个选手的得分
int scoresun = accumulate(dscore.begin(), dscore.end(), 0);
int scoreavg = scoresun / dscore.size();
mapSpeaker[*it].m_score[index] = scoreavg;//选手得分存入容器中
multimapGroup.insert(pair<int, int>(scoreavg, *it));//选手得分 选手编号
}
//处理分组
if (tmpCount % 6 == 0)
{
cout << "小组的比赛成绩" << endl;
//得分 编号
for (multimap<int, int, greater<int>>::iterator mit = multimapGroup.begin(); mit != multimapGroup.end(); mit++)
{
//编号 姓名 得分
cout << mit->second << "\t" << mapSpeaker[mit->second].m_name << "\t" << mit->first << endl;
}
//前3名晋级
while (multimapGroup.size() > 3)
{
multimap<int, int, greater<int>>::iterator it1 = multimapGroup.begin();
v2.push_back(it1->second);//把前3名 放到 v2 晋级名单中
multimapGroup.erase(it1);//迭代器自动下移
}
multimapGroup.clear();//用完清除
}
}
return 0;
}
//查看比赛结果
int speech_contest_print(int index, vector<int> &v, map<int, Speaker> &mapSpeaker)
{
printf("第%d 轮 晋级名单\n", index + 1);
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << "参赛编号: " << *it <<"\t" << mapSpeaker[*it].m_name << "\t" << mapSpeaker[*it].m_score[index] << endl;
}
return 0;
}
void test()
{
//容器的设计
map<int, Speaker> mapSpeaker;//参加比赛的选手
vector<int> v1; //第1轮 演讲比赛名单
vector<int> v2; //第2轮 演讲比赛名单
vector<int> v3; //第3轮 演讲比赛名单
vector<int> v4; //最后前3名演讲比赛名单
//产生选手 得到第一轮选手的比赛名单
GenSpeaker(mapSpeaker, v1);
//第1 轮 选手抽签 选手比赛 查看比赛结果
cout << "\n\n\n任意键,开始第1轮比赛" << endl;
cin.get();
speech_contest_draw(v1);
speech_contest(0, v1, mapSpeaker, v2);
speech_contest_print(0, v2, mapSpeaker);
//第2 轮 选手抽签 选手比赛 查看比赛结果
cout << "\n\n\n任意键,开始第2轮比赛" << endl;
cin.get();
speech_contest_draw(v2);
speech_contest(1, v2, mapSpeaker, v3);
speech_contest_print(1, v3, mapSpeaker);
//第3 轮 选手抽签 选手比赛 查看比赛结果
cout << "\n\n\n任意键,开始第3轮比赛" << endl;
cin.get();
speech_contest_draw(v3);
speech_contest(2, v3, mapSpeaker, v4);
speech_contest_print(2, v4, mapSpeaker);
}
#endif
int main()
{
test();
cout << "------";
system("pause");
}
c++声明周期管理
最新推荐文章于 2022-10-10 20:16:01 发布