小甲鱼学习课程
02
#include <iostream>
头文件 iostream io+stream 翻译为IO流 输入输出流
using namespace std; 引入命名空间
c++标准库所使用的所有标识符都是在同一个特殊的名字空间std中来定义的;
偷懒使用这句话;
std::cout <<"第一个int元素结果是:" << data[0]<<endl;
std::cout 可以直接使用 cout 代替
endl 结束输出流,没有这个就会一直流出;
cout输出流例子
#include <iostream>
using namespace std;
int main()
{
int data[] = {99,1,2,3,4,5};
int size = sizeof(data) / sizeof(data[0]);
std::cout <<"第一个int元素结果是:" << data[0]<<endl;
}
03
cin输入流
配合>> 就是已经重载,重载类似于替代原有含义,替代了右移,变成了提取信息;
提取输入流的时候,提取正确的类型返回true,否则falsh;
如int i;
cin>>i,提取到3.14小数点,就返回falsh,因为i是int类型;但是可以提取到3;
可以提取字符串、int、float、double类型
cin.peek();检查当前流的字符
cin.get();读取流的下一个字符
#include <iostream>
using namespace std;
int main()
{
int i,sum=0;
while (cin>>i)
{
sum += i;
while (cin.peek() == ' ')//如果为空格,一直获取空格
{
cin.get();
}
if (cin.peek() == '\n')
{
break;
}
}
std::cout << sum;
}
cin.ignore(n);//忽略 n个字符
cin.ignore(n,‘ x’);//忽略 n个字符,n个字符以内遇到x就结束忽略
cin.getline(buf, n);//保存n个字符到buf
int main()
{
char buf[20];
cin.ignore(7);//忽略 7个字符
cin.getline(buf, 10);//保存10个字符
std::cout << buf<<endl;
}
cin.read(buf,10);//读取10个字符到buf
cin.gcount() //返回输入流的字符计数值
int main()
{
char buf[20];
cin.read(buf,10);//读取10个字符到buf
std::cout << cin.gcount() << endl;//输出输入流的字符计数
}
04
在工程下面新建一个123.txt文本
文件输出流
ofstream out;//流出到文件
out.open("123.txt"); 这个流指定文本,加载到程序内存
out.close(); 关闭 这个流指定文本
#include <iostream>
#include<fstream>//文件数据流
using namespace std;
int main()
{
ofstream out;//输出文件数据流
out.open("123.txt");//输出流;打开文件,文件流指向这个文件
if (!out)
{
cerr << "打开文件失败" << endl;
return 0;
}
for(int i=0;i<10;i++)
{
out << i;//文件输出流,流入指定位置
}
out << endl;//停止流
out.close();//关闭文件输出流
std::cout << "Hello World!\n";
}
ifstream in; 输入文件流对象;用来流入到文件
in.open("123.txt");//输入文件流,这个流指定文本,从这个文件流入,加载到程序内存
if ( !in ) 如果打开文件失败返回false
#include <iostream>
#include<fstream>
using namespace std;
int main()
{
ifstream in;//输入文件流
in.open("123.txt");//输入文件流,指定文本,从这个文件流入
if (!in)
{
cout << "打开文件失败" << endl;
return 0;
}
char i;
while (in>>i)//文件流入给字符i
{
cout << i;//
}
in.close();//关闭这个流的文本
std::cout << "Hello World!\n";
}
ifstream in("123.txt"); 也可以构建对象的时候直接打开文件,指定流文件
ofstream out("123.txt");
fstream in("123.txt",int open_mode);//二参数打开
//open_mode 可以如下
ios::in;//打开一个可读文件
ios::out;//打开一个可写文件
ios::binary;//以二进制的形式打开一个文件
ios::app;//写入的所有数据将被追加到文件的末尾
ios::trunc;//删除文件原来已经存在的内容
ios::_Nocreate;//如果要打开的文件不存在,那么以此参数调用open函数将无法进行
ios::_Noreplace;//如果要打开的文件已存在,试图用open打开时返回错误在c++库中如下:所以可以多种模式用 并运算符使用
static constexpr _Openmode in = static_cast<_Openmode>(0x01); static constexpr _Openmode out = static_cast<_Openmode>(0x02); static constexpr _Openmode ate = static_cast<_Openmode>(0x04); static constexpr _Openmode app = static_cast<_Openmode>(0x08); static constexpr _Openmode trunc = static_cast<_Openmode>(0x10); static constexpr _Openmode _Nocreate = static_cast<_Openmode>(0x40); static constexpr _Openmode _Noreplace = static_cast<_Openmode>(0x80); static constexpr _Openmode binary = static_cast<_Openmode>(0x20);
fstream fp("123.txt",ios::in|ios::out);文件流对象,输入输出流模式,指定文件
fp.seekg( ) 文件定位
#include <iostream>
#include<fstream>
using namespace std;
/*
seekg是对输入文件定位,它有两个参数:第一个参数是偏移量,第二个参数是基地址。
ios::end是代表流的结尾,0代表偏移量。f.seekg(0, ios::end);意思是直接跳到当前流的结尾,略过流内的所有数据*/
int main()
{
fstream fp("123.txt",ios::in|ios::out);//
if (!fp)
{
cerr << "打开文件失败" << endl;
}
fp << "145a4da4dsd1435";//流出到文件
static char str[30];
fp.seekg(ios::beg); //ios::end; 流文件结尾
fp >> str;//流入到数组
cout << str << endl;
fp.close();
return 0;
}
05输入输出总结
1
#include <iostream>
int main()
{
char answer;
std::cout << "请问可以格式化您的硬盘吗?【Y/N】" << "\n";
std::cin >> answer;//缓冲区里面:一个是输入字符,和回车
switch (answer)
{
case 'y':
case 'Y':
std::cout << "格式化了\n";
break;
case 'n':
case 'N':
std::cout << "不会格式化\n";
break;
default:
std::cout << "输入错误\n";
break;
}
std::cin.ignore(10,'\n');//忽略10个字符,直到10个字符内存在回车 ;为了忽略那个输入回车
std::cin.get();//等待输入结束
std::cout << "Hello World!\n";
}
//输入检测函数
std::cin.eof();//输入到文件的末尾返回true
std::cin.fail();//如果cin无法工作,返回true
std::cin.bad();//如果cin因为比较严重的原因而无法工作,返回true
补充:
cin.clear()//清除标示符
重置错误标识符:当输入操作失败时,例如输入了一个非法的字符,cin会设置错误标识符。此时,可以使用cin.clear()来重置这些标识符,使其回到无错误状态
06:函数的重载
定义:同样的名字再定义一个有着不同参数但是有着同样用途的函数;
参数个数上的不同,也可以是参数类型的不同;
只能通过不同的参数重载,不能通过返回值重载
void sum_add_100();//重载如下
#include <iostream>
void sum_add_100(double a);
void sum_add_100(int a);
int main()
{
double z = 0;
int b = 0;
std::cout << "请输入double型";
std::cin >> z;
std::cin.ignore(10, '\n');
sum_add_100(z);
std::cout << "请输入int型";
std::cin >> b;
std::cin.ignore(10, '\n');
sum_add_100(b);
std::cout << "Hello World!\n";
}
void sum_add_100(double a)
{
a += 100;
std::cout << a<<'\n';
}
void sum_add_100(int a)
{
a += 100;
std::cout << a << '\n';
}
数组类型
指针类型
结构类型
7-9数组类型
数组类型:和c语言一样;
字符串类型:本质也是字符存在数组;但是c++使用不一样
std::string str;//构建字符串对象
std::getline(std::cin,str);//获取行,用cin的方式,获取到字符串对象
#include <iostream>
#include<string>
int main()
{
std::string str;//构建字符串对象
std::cout << "请输入一串字符: \n";
// std::cin >> str;
std::getline(std::cin,str);
std::cout << str<<'\n';
std::cout << "Hello World!\n";
}
获取子串
std::string str = "Hello, World!";
std::string subStr1 = str.substr(7, 5);//获取子串(起始字节,长度 );指定位置
比较字符串
直接用比较运算符,比较两个字符串对象,按字符串靠前的优先比较;
ac比较ab 前面a都相等 第二个字节c大 所以ac大ab
str1 ==str2
str 1!=str2
添加字符串
str1.append("xxx ")//可以多级引用append
搜索字符串
str.find(子串)// 返回搜索到到起始索引
#include <iostream>
#include<string>
int main()
{
std::string str;//构建字符串对象
std::cout << "请输入一串字符: \n";
// std::cin >> str;
std::getline(std::cin,str);
std::cout << str<<'\n';
std::cout << "Hello World!\n";
std::string str1="aaa bbb ccc";
std::string str2 = "ccc";
std::string str3 = "aaa bbb ccc";
std::string st= str1.substr(3,6);//提取第三个元素开始的后6个元素
std::cout <<"主串为:"<<str1 << "字串为:" << st<<'\n';
if (str1> str2)//比靠前的字符大小
{
std::cout << str1 << "最大\n";
}
else
{
std::cout << str2 << "最大\n";
}
str3.append("123456");//拼接字符串
std::cout << str3;
uint16_t t=str1.find(str2);//返回索引值
std::cout <<'\n' << t;
}
指针类型08
取地址和c语言一样
int a;
&a;
对齐也是c语言一样,32位系统4字节对齐
指针声明和c语言一样
地址使用和c语言一样 int a ;int *p=&a;必须同类: 形参使用也是一样;
#include <iostream>
int main()
{
int a = 1;
double b = 2;
float c = 3;
char d = 4;
int *p1 = &a;
double *p2 = &b;
float *p3 = &c ;
char *p4 = &d;
std::cout << a<<'\n';
std::cout << b << '\n';
std::cout << c << '\n';
std::cout << d << '\n';
*p1 = 5;
*p2 = 6;
*p3 = 7;
*p4 = 8;
std::cout << p1 << '\n';
std::cout << p2 << '\n';
std::cout << p3 << '\n';
std::cout << p4 << '\n';
std::cout << "Hello World!\n";
}
c++的地址++和c一样;
指针的地址++,指向下一个同类型的数据;
int a【10】 ;
int *p=a;
p++;此时p=&a【1】;
指针++的不是地址+1,而是偏移sizeof(int)个字节偏移量;
10
结构和c的结构体是一样使用的,包括结构指针
11、
形参使用,地址传递,和c一样。
引用特别说明
void aaa ( int * ,int * )
{ }
在c++中引用是这样的
int a,b;
aaa ( a ,b );
其实传递进去给形参的是地址,等效aaa ( &a ,&b );
13联合体、结构体、类型别名
和c语言一样使用,不做描述
struct MyStruct//结构体
{
int a;
double b;
char c;
};
union MyUnion//联合体
{
int a;
double b;
};
typedef int uint32;//类型别名
14、c++的对象
对象和结构的区别;
结构:包含各种类型的变量;
对象:包含各种函数、和变量;
设计对象的时候引用class关键字创建类,和结构形状差不多;
将变量称之为属性,函数称之为方法;
14造车,创建类:类的实际应用;
一个awm型号的车;
属性(c变量):车身颜色、油
方法(c函数):改变颜色、加油;
类的声明和定于;
#include <iostream>
class MyClass_che
{
public:
MyClass_che();
~MyClass_che();
std::string yanse;
void gaibian_yanse(std::string);
int you;
void gaibian_you(int);
private:
};
void MyClass_che::gaibian_yanse(std::string str)// ::表域符 ,指示实在这个类里面的方法/或属性
{
MyClass_che::yanse = str;
}
void MyClass_che::gaibian_you(int you)
{
MyClass_che::you = you;
}
类定义时候:不能对常量赋值,除非是加static
awm a;创建了一个对象a属于awm型号的车
以上结合就理解了这就是对象;这个对象有颜色、油属性;有改变颜色、改变油的方法;
作用域 ::表域符 ,指示是在这个类里面的方法/或属性
同类对象可以赋值;
awm a,b;
b=a;
16构造器
构造器和通常方法的主要区别;
-构造器的名字必须和它所在的类的名字一样;
-系统在创建某个类的实例时会第一时间自动调用这个类的构造器;
-构造器永远不会返回任何值
构造器如下
构造器定义如下;相当于一个函数,没有viod,且在声明创建一个对象后被执行;(相当于c里面的 回调函数,创建一个对象就回调)
MyClass_che::MyClass_che()
{
}
每个类至少一个构造器;没写构造器,编译器也会给你定义一个构造器( 内容为空 );
析构器
属性和构造器一样;不过前面加~波浪线
程序结束时调用:该程序使用内存,就在析构器里面释放内存
应用
比如构造器打开文本:打开文本需要内存
,析构器关闭文本 ; 关闭文本就释放了内存
#include <iostream>
#include<fstream>
class MyClass_che
{
public:
MyClass_che();
~MyClass_che();
std::string yanse;
void gaibian_yanse(std::string);
std::fstream pf;
int you;
void gaibian_you(int);
private:
};
MyClass_che::MyClass_che()
{
pf.open("123.txt", std::ios::in | std::ios::out);
}
MyClass_che::~MyClass_che()
{
pf.close();
}
17this的继承和类的继承
this指针,表当前方法的所属类;
void MyClass_che::gaibian_you(int you)
{
MyClass_che::you = you;
this->you = you;//此时的this等效于MyClass_che,右边的you代表形参,左边代表这个类的属性
}
基类:
子类;
两个类的描述:
基类是公共有的,子类继承基类,并且多于基类的属性和方法;
例如;
动物是基类
{
吃饭、睡觉
}
甲鱼子类、猪是子类
甲鱼
{
基类的属性和方法
独特属于甲鱼的属性:游泳
}
猪
{
基类的属性和方法
独特属于猪的属性:上树
};
#include <iostream>
class dongwu//基类
{
public:
dongwu();
~dongwu();
void eat();
private:
};
void dongwu::eat()//基类方法
{
std::cout << "我在吃饭" << std::endl;
}
dongwu::dongwu()
{
}
dongwu::~dongwu()
{
}
class pig:public dongwu//pig 继承于(分号) dongwu
{
public:
pig();
~pig();
void shangshu();//属于猪的独特属性
private:
};
pig::pig()
{
}
pig::~pig()
{
}
void pig::shangshu()
{
std::cout << "上树了" << std::endl;
}
int main()
{
pig pig1;//构建一只猪1对象,类别是子类pig
pig1.eat();//这是继承的基类的方法
pig1.shangshu();//这是子类特有的
std::cout << "Hello World!\n";
}
18子类的构造器和析构
创建对象时:
先执行父类的构造器
才到子类的构造器;
销毁对象时:
子类的最后一条语句结束后调用基类的析构器;
- 首先调用子类的析构器。
- 然后调用父类的析构器。
带参数的构造器声明
父类,基类
class dongwu
{
public:
dongwu( std::string N_name);//带参的基类析构函数
~dongwu();
private:
};
子类
class pig:public dongwu//继承父类的子类
{
public:
pig(std::string N_name);//带参的子类析构函数
~pig();
private:
};
构造器定义
带参子类构造器的定义
//子构造器的形式 子构造器 :父类构造器(不带类型 的参数)
dongwu::dongwu(std::string N_name)
{
name = N_name;
}
这个name = N_name;
个人认为是:相当于tihs->name = N_name; 初始化了tihs->name;
然后再继承给子类
pig::pig(std::string N_name):dongwu::dongwu( N_name)//子构造器的形式 子构造器 :父类构造器(不带类型 的参数)
{
}
子类析构器
编译器自动添加;可不用处理;
19访问控制
类的属性和方法有访问控制
访问控制可以在编译的时候编译器可以检查到合法性;
在声明类的时候,访问控制如下关键字
public: 公共 此下的属性和方法 可以被任何代码访问
protected: 保护 此下的属性和方法,可以被这个类本身方法和子类方法访问
1、外部代码无法通过过子类去调用父类的保护
private: 私有 此下的属性和方法,只有这个类本身可以访问
class dongwu
{
public://任何代码都可以访问
dongwu( std::string N_name);//带参的基类析构函数
~dongwu();
std::string name;
protected://子类方法内和这个类方法内可以访问
int a;
private://只能 dongwu temp;由这个类的 temp 访问
int b;
};
main函数里面无法找到a和b;a被保护 b是私有;
在pig类方法里面,可以引用父类的被保护的a;
pig::pig(std::string N_name):dongwu::dongwu( N_name)//子构造器的形式 子构造器 :父类构造器(不带类型 的参数)
{
//name = N_name;
this->a = 0;
}
20覆盖函数和重载
覆盖如下:
子类继承了父类的eat()方法,
子类中又声明且定义了eat()的方法,此时子类调用的eat方法,就会覆盖继承父类的eat();
所以子类调用的时候,调用的方法其实就是子类的eat();
class dongwu
{
public://任何代码都可以访问
dongwu( std::string N_name);//带参的基类析构函数
~dongwu();
std::string name;
void eat();
protected://子类方法内和这个类方法内可以访问
int a;
private://只能 dongwu temp;由这个类的 temp 访问
int b;
};
void dongwu::eat()//父类的
{
std::cout << " eat \n";
}
class pig:public dongwu//继承父类的子类
{
public:
pig(std::string N_name);//带参的子类析构函数
~pig();
void eat();
private:
};
void pig::eat()//子类的
{
std::cout << "吃到了 大蛋糕";
}
重载
通过重载等效覆盖,通过改变子类的参数个数,或者参数类型,就可以保留继承父类的eat()
参考其中的章节: 06:函数的重载
class dongwu
{
public://任何代码都可以访问
dongwu( std::string N_name);//带参的基类析构函数
~dongwu();
std::string name;
void eat();
protected://子类方法内和这个类方法内可以访问
int a;
private://只能 dongwu temp;由这个类的 temp 访问
int b;
};
void dongwu::eat()//父类的
{
std::cout << " eat \n";
}
class pig:public dongwu//继承父类的子类
{
public:
pig(std::string N_name);//带参的子类析构函数
~pig();
void eat(int);
private:
};
void pig::eat(int)//子类的
{
dongwu::eat();//只要前面文本定义了这个 方法就可以直接这样调用
std::cout << "吃到了 大蛋糕";
}
补充: dongwu::eat();//只要前面文本”定义 “了这个 方法就可以直接这样调用
21友元关系
在aa类声明的时候添加
friend class bb;//指定友元为bb,所以类bb可以访问aa的保护
#include <iostream>
class aa
{
public:
protected:
int k1;
private:
friend class bb;//指定友元为bb,所以类bb可以访问aa的保护
};
class bb
{
public:
void set(class aa* k);
protected:
int y1;
private:
};
void bb::set(class aa * k)//传递aa的类指针
{
k->k1 = 0;//调用了aa的保护属性,无错误,所以可以调用
}
int main()
{
std::cout << "Hello World!\n";
}
22、静态属性和静态方法
静态成员
1、需要在外部定义和初始化
2、只需要在类里面声明;
3、静态成员先于类,不是独特地属于任何类;
静态成员是所有同类对象共享的,再静态方法里面不能访问普通成员;
普通方法可以访问静态成员和方法;
this指针
1、this指针每个类自动生成的私有成员
2、当一个对象被生成的时候,该对象的this指针就指向了对象的首地址
3、任何方法都可以用this指针,这个this指针是隐藏传送到方法里面的;get()其实是get(类指针* this );方法被调用指针就传入;
4、this可以指向每个成员和方法
结论:所以静态方法不属于任何对象,所以无法确定为对象地址,所以无法使用this指针;
调用方法坚持使用 类::方法;可以提高代码阅读性
如cat::get();
静态成员共享例子
#include <iostream>
#include <string>
using namespace std;
class dongwu
{
public:
dongwu(std::string N_name);
~dongwu();
static int get_cout();
int get();
std::string name;
private:
static int cout;
int a;
};
int dongwu::cout = 0;
int dongwu::get_cout()
{
// a++; 会编译错误
return cout;
}
int dongwu::get()
{
return cout;
}
dongwu::dongwu(std::string N_name)
{
name = N_name;
cout++;
std::cout << name<<cout;
}
dongwu::~dongwu()
{
cout--;
}
class cat:public dongwu
{
public:
cat(std::string N_name);
};
cat::cat(std::string N_name):dongwu::dongwu(N_name)
{
}
int main()
{
cat a1("小猫");
cat a2("小猫");
std::cout << '\n' << a1.get()<<'\n';
{
cat a3("小猫");
cat a4("小猫");
std::cout << '\n' << a1.get_cout() << '\n';
}
std::cout << '\n' << a1.get_cout() << '\n';
}
24虚方法;
虚方法关键字 在类的声明里面的方法添加关键字virtual
class dongwu
{
public:
dongwu(std::string N_name);
~dongwu();
std::string name;
virtual void play();///虚方法
private:
static int cout;
};
应用场景:
//指针是基类,创建的内存是子类;导致调用子类方法不能覆盖,必须用虚方法解决
#include <iostream>
#include <string>
using namespace std;
class dongwu
{
public:
dongwu(std::string N_name);
~dongwu();
std::string name;
virtual void play();///虚方法
private:
static int cout;
};
dongwu::dongwu(std::string N_name)
{
name = N_name;
}
void dongwu::play()
{
std::cout << "动物在玩";
}
dongwu::~dongwu()
{
}
class cat:public dongwu
{
public:
cat(std::string N_name);
void play();
};
cat::cat(std::string N_name) :dongwu(N_name)
{
}
void cat::play()//子类方法
{
std::string str=this->name;
str.append("这只猫再玩毛线");
std::cout << str;
}
int main()
{
dongwu* p = new cat("小花");//指针是基类,创建的内存是子类;导致调用子类方法不能覆盖,必须用虚方法解决
p->play();//如果基类不加virtual 虚方法关键字 ,方法是基类方法,子类没有成功覆盖
}
补充:
new 创建内存
delete 删除内存
new cat ;创建了一个子类内存
delete :释放了一个子类的内存
25、纯虚方法
纯虚方法:
在虚方法后面=0;
作用,优化代码,告诉编译器,不需要寻找执行实现,这是一个纯虚方法,任何都可以覆盖,也可以说不用可以去覆盖了,每个继承的都可以无顾虑的实现覆盖;
多态性:
一个名字,可以执行不同的操作;
编译时的多态:通过重载实现,编译时运行快
运行时的多态:通过虚函数实现,运行时灵活和抽象
析构函数就是虚方法;
是为了当基类指针删除子类对象时,正确调用子类析构函数;
思路(仅仅为了理解):相当于为了覆盖子类方法,用虚方法,使得调用方法时是执行子类方法,而不是基类的方法;
特别说明:基类被继承后,基类的析构才是虚函数;
26、运算符重载
以下5个运算符不允许重载
. (成员访问运算符)
.* 成员指针访问运算符(本人认为 ->)
::域运算符
sizeof( ) (尺寸运算符)
?: (条件运算符)
运算符重载形式:
函数类型 operator 运算符(参数列表);
Complex operator +(Complex&d);//重载运算符 相当于返回值类型为Complex类
当 Complex类 遇到加号 且后面为同类Complex,运算符执行如下(函数运算)
例子如下备注,仅个人理解
#include <iostream>
class Complex
{
public:
Complex();
~Complex();
Complex(double r, double i);
Complex operator +(Complex&d);//重载运算符 相当于返回值类型为Complex类 当 Complex类 遇到加号 且后面为同类Complex,运算符执行如下运算
void print();
private:
double real;
double imag;
};
Complex::Complex()
{
}
Complex::~Complex()
{
}
Complex::Complex(double r, double i)//重载函数
{
real = r;
imag = i;
}
Complex Complex::operator +(Complex& d)//括号里面的是类对象,传入对象 运算符重载
{
Complex c;
c.imag = imag + d.imag;
c.real = real + d.real;
return c;
}
void Complex::print()
{
std::cout << "(" << real << "," << imag << "i)";
}
int main()
{
Complex c1(1.0, 2.0), c2(3.0, -4.0), c3;
c3 = c1 + c2;//该对象是Complex类遇到+,发生了重载,使用指定的运算
int a, b=8, c=9;
a = b + c;
c3.print();
std::cout << "Hello World!\n"<<a;
}
小鱼理解:
c1+c2
c1.operator+(c2);编译器理解为,相当于调用一个函数得到返回值
运算符重载作为类友元函数
运算符重载
Complex operator +(Complex &a, Complex &b)//在类中被声明了友元,所以可以访问私有;这同时是个运算符重载
{
return Complex(a.real + b.real, a.imag + b.imag);//
}
在类里面声明为友元
class Complex
{
public:
Complex();
~Complex();
Complex(double r, double i);
// Complex operator +(Complex&d);//重载运算符 相当于返回值类型为Complex类 当 Complex类 遇到加号 且后面为同类Complex,运算符执行如下运算
friend Complex operator +(Complex& a, Complex& b);//声明为该类的友元函数
void print();
private:
double real;
double imag;
};
例子
#include <iostream>
class Complex
{
public:
Complex();
~Complex();
Complex(double r, double i);
// Complex operator +(Complex&d);//重载运算符 相当于返回值类型为Complex类 当 Complex类 遇到加号 且后面为同类Complex,运算符执行如下运算
friend Complex operator +(Complex& a, Complex& b);//声明为该类的友元函数
void print();
private:
double real;
double imag;
};
Complex::Complex()
{
}
Complex::~Complex()
{
}
Complex::Complex(double r, double i)
{
real = r;
imag = i;
}
Complex operator +(Complex &a, Complex &b)//在类中被声明了友元,所以可以访问私有;这同时是个运算符重载
{
return Complex(a.real + b.real, a.imag + b.imag);//
}
void Complex::print()
{
std::cout << "(" << real << "," << imag << "i)";
}
int main()
{
Complex c1(1.0, 2.0), c2(3.0, -4.0), c3;
c3 = c1 + c2;//该对象是Complex类遇到+,发生了重载,使用指定的运算
int a, b=8, c=9;
a = b + c;
c3.print();
std::cout << "Hello World!\n"<<a;
}
28再次重载<< (对数据流)
原理和26差不多,且用得不多;
29、多继承
当一个对象有多个身份的时候,就有了多种关系
一个人既可以是学生也可以是老师;基类是人,子类是老师、学生;还有一个子类既是学生也是老师;
因为多继承:所以可以访问不同父类的保护成员;
多继承的类声明
class 子类: public 父类, public 父类 //(class 子类:分号 public 父类, 逗号public 父类)
多继承的析构器
子类::子类析构(std::string N_name) : 分号 父类析构( N_name), 逗号 父类析构(N_name)
{
}
N_name为参数;
techer_student::techer_student(std::string N_name):techter( N_name), student(N_name)
{
}
例子
#include <iostream>
#include<string>
//*********************************************************************
class peson
{
public:
peson(std::string N_name);
protected:
std::string name;
private:
};
peson::peson(std::string N_name)
{
}
//*******************************************************************************
class techter:public peson
{
public:
techter(std::string N_name);
protected:
std::string tech_banji="我作为老师,教大学三年级";
private:
};
techter::techter(std::string N_name):peson(N_name)
{
}
//**********************************************************************************
class student:public peson
{
public:
student(std::string N_name);
protected:
std::string student_banji="我作为学生,在读清华博士";
private:
};
student::student(std::string N_name):peson(N_name)
{
}
//******************************************************
class techer_student: public techter, public student
{
public:
techer_student(std::string N_name);
void jieshao();
protected:
std::string techer_student_banji="我是学生也是老师";
private:
};
techer_student::techer_student(std::string N_name):techter( N_name), student(N_name)
{
}
void techer_student::jieshao()
{
std::cout << student::student_banji << '\n' << techter::tech_banji << '\n' << techer_student::techer_student_banji << '\n';
//多继承 既可以使用student 的保护 也可以使用techter的保护
}
int main()
{
techer_student lin("彭冠霖");
lin.jieshao();
std::cout << "Hello World!\n";
}
30、虚继承
虚继承的作用
虚继承的主要作用是解决多重继承中的“菱形继承”问题。在多重继承中,当一个类继承了多个父类,而这些父类又继承了同一个基类时,会导致子类中存在多份基类的拷贝,这会造成对基类成员的访问存在多义性
void techer_student::jieshao()
{
std::cout << student::student_banji << '\n' << techter::tech_banji << '\n' << techer_student::techer_student_banji << '\n';
techer_student::stud() ;//调用了名字
techer_student::tech();调用了名字 本来就是同一个人,只能用同一个名字,在继承过来后应该统一名字
//多继承 既可以使用student 的保护 也可以使用techter的保护
}
对基类成员的访问存在多义性
造成了继承了多个名字;
如下程序;
// ConsoleApplication1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include<string>
//*********************************************************************
class peson
{
public:
peson(std::string N_name);
protected:
std::string name;
private:
};
peson::peson(std::string N_name)
{
}
//*******************************************************************************
class techter:public peson
{
public:
techter(std::string N_name);
protected:
std::string tech_banji="我作为老师,教大学三年级";
void tech();
private:
};
techter::techter(std::string N_name):peson(N_name)
{
name = N_name;
}
void techter::tech()
{
std::cout << "我是" << name;
}
//**********************************************************************************
class student:public peson
{
public:
student(std::string N_name);
protected:
std::string student_banji="我作为学生,在读清华博士";
void stud();
private:
};
student::student(std::string N_name):peson(N_name)
{
name = N_name;
}
void student::stud()
{
std::cout << "我是" << this->name;
}
//******************************************************
class techer_student: public techter, public student
{
public:
techer_student(std::string N_name1, std::string N_name2);
void jieshao();
protected:
std::string techer_student_banji="我是学生也是老师";
private:
};
techer_student::techer_student(std::string N_name1,std::string N_name2):techter( N_name1), student(N_name2)
{
}
void techer_student::jieshao()
{
std::cout << student::student_banji << '\n' << techter::tech_banji << '\n' << techer_student::techer_student_banji << '\n';
techer_student::stud() ;//调用了名字
techer_student::tech();调用了名字 本来就是同一个人,只能用同一个名字,在继承过来后应该统一名字
//多继承 既可以使用student 的保护 也可以使用techter的保护
}
int main()
{
techer_student lin("彭冠霖","小彭");
lin.jieshao();
std::cout << "Hello World!\n";
}
虚继承,在每个继承的时候,在前面添加virtual
在子类的构造器添加基类继承 和父类
使用虚继承后的程序:和以上程序相比,没啥体现差别,除了关键字;
// ConsoleApplication1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include<string>
//*********************************************************************
class peson
{
public:
peson(std::string N_name);
protected:
std::string name;
private:
};
peson::peson(std::string N_name)
{
}
//*******************************************************************************
class techter: virtual public peson
{
public:
techter(std::string N_name);
protected:
std::string tech_banji="我作为老师,教大学三年级";
void tech();
private:
};
techter::techter(std::string N_name):peson(N_name)
{
name = N_name;
}
void techter::tech()
{
std::cout << "我是" << name;
}
//**********************************************************************************
class student: virtual public peson
{
public:
student(std::string N_name);
protected:
std::string student_banji="我作为学生,在读清华博士";
void stud();
private:
};
student::student(std::string N_name):peson(N_name)
{
name = N_name;
}
void student::stud()
{
std::cout << "我是" << this->name;
}
//******************************************************
class techer_student:virtual public techter, virtual public student
{
public:
techer_student(std::string name);
void jieshao();
protected:
std::string techer_student_banji="我是学生也是老师";
private:
};
techer_student::techer_student(std::string name):techter(name), student(name),peson(name)
{
}
void techer_student::jieshao()
{
std::cout << student::student_banji << '\n' << techter::tech_banji << '\n' << techer_student::techer_student_banji << '\n';
techer_student::stud() ;//调用了名字
techer_student::tech();调用了名字 本来就是同一个人,只能用同一个名字,在继承过来后应该统一名字
//多继承 既可以使用student 的保护 也可以使用techter的保护
}
int main()
{
techer_student lin("彭冠霖");
lin.jieshao();
std::cout << "Hello World!\n";
}
再对比如下,效果一样;所以没体现出虚继承的差别;很疑惑
// ConsoleApplication1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include<string>
//*********************************************************************
class peson
{
public:
peson(std::string N_name);
protected:
std::string name;
private:
};
peson::peson(std::string N_name)
{
}
//*******************************************************************************
class techter: public peson
{
public:
techter(std::string N_name);
protected:
std::string tech_banji="我作为老师,教大学三年级";
void tech();
private:
};
techter::techter(std::string N_name):peson(N_name)
{
name = N_name;
}
void techter::tech()
{
std::cout << "我是" << name;
}
//**********************************************************************************
class student: public peson
{
public:
student(std::string N_name);
protected:
std::string student_banji="我作为学生,在读清华博士";
void stud();
private:
};
student::student(std::string N_name):peson(N_name)
{
name = N_name;
}
void student::stud()
{
std::cout << "我是" << this->name;
}
//******************************************************
class techer_student: public techter, public student
{
public:
techer_student(std::string name);
void jieshao();
protected:
std::string techer_student_banji="我是学生也是老师";
private:
};
techer_student::techer_student(std::string name):techter(name), student(name)//,peson(name)
{
}
void techer_student::jieshao()
{
std::cout << student::student_banji << '\n' << techter::tech_banji << '\n' << techer_student::techer_student_banji << '\n';
techer_student::stud() ;//调用了名字
techer_student::tech();调用了名字 本来就是同一个人,只能用同一个名字,在继承过来后应该统一名字
//多继承 既可以使用student 的保护 也可以使用techter的保护
}
int main()
{
techer_student lin("彭冠霖");
lin.jieshao();
std::cout << "Hello World!\n";
}
31、32错误处理和调试
错误分为编译错误和运行错误
编译时错误
经验:
- 在编程的时候保持一种风格、一旦决定要如何命名变量和函数、如何缩进、何种格式编写代码;
- 认真对待编译器的错误和警告
- 先画流程图
- 编译错误、先完整的看一遍代码、再矫正错误;
- 注意基本语法
- 把可能有问题的代码注释掉
- 换一个环境或者开发工具:可能是杀毒软件,什么之类的影响;
- 头文件包含
- 留意变量的作用域和命名空间
- 休息一下:效率下降后需要休息;
- 多使用调试工具
- 把调试好的程序控制保存起来;把代码划分成模块,来搭建新程序可以减少很多时间;
运行时的错误
逻辑漏洞;bug同一个产品一个好一个坏;时有时无没法确定;
经验;
- 保持一种良好的编程风格
- 多注释,注释好,特别是参数变化;简单代码,不要啰嗦
- 操作符优先级,括号很保险
- 输入输出合法性检查
- 不要做任何假设:用户不会按照你的想法去使用你的程序;
- 划分单元模块,这样测试方便
让函数返回错误代码
输入输出错误码
使用climits头文件
给定操作系统上的取值范围:每种数据的上下限给我们使用
如:SHORT_MAX 、SHORT_MIN分别代表短整类型的最大取值和最小取值;
例子:使用使用climits头文件
#include <iostream>
#include<climits>//引用c限制头文件
class ftory
{
public:
ftory(unsigned short num);
~ftory();
unsigned long get_ftory();
bool inRange();
private:
unsigned short number;
};
ftory::ftory(unsigned short num)
{
this->number = num;
std::cout << "输入参数为" << this->number<<'\n';
}
ftory::~ftory()
{
}
unsigned long ftory::get_ftory()
{
unsigned long sun = 1;
for (int i=1;i<= number;i++)
{
sun *= i;
}
return sun;
}
bool ftory::inRange()
{
unsigned long temp= ftory::get_ftory();
if (temp >= USHRT_MAX )//这是climit头文件里面的限制参数
return false; //超出限制就返回false
else
return true;
}
int main()
{
ftory a(20);
std::cout<< a.get_ftory()<<'\n';
std::cout << a.inRange() << '\n';
std::cout << "Hello World!\n";
}
33、运行错误、异常捕获
#include<cassert>//前面的c表示时c库里面的
assert(条件判断);条件不成立时,提示错误,可以用这个函数定位运行错误
输出窗口可以定位文件和行数
#include <iostream>
#include<cassert>
int main()
{
uint16_t i;
std::cin >> i;
assert(i<100);
std::cout << "Hello World!\n";
}
#include
捕获异常
运行错误分为两种
- 开发 调试 测试过程中,尽可能的查找和定位错误;使用函数assert等等;
- 和最终用户有关,尽可能的把用户感受放第一位;用户不懂技术,要用户能轻松使用;输出也不能全部开放,看不懂只会增加用户麻烦;
大公司微软都不可能没有bug,关键在于后续升级
捕获异常:
为了对付潜在的编程错误。异常:就是与期望不相符合的反常现象
捕获异常的的基本使用思路
- 安排c++代码去尝试某件事 (try 语句)
- 如果发生问题就抛出一个异常
- 再安排一些代码捕获这个异常并进行相应的处理(catch语句)
Try 至少配一条 catch语句,否则中止程序
Catch (int a) {.........}//捕获int型
Catch (bool) {.........}//捕boolt型
Catch (char) {.........}
//Catch 可以捕获任何类型的
C++ 中Exception 的文件 声明exception 的基类。可以用这个基类来做私人的子类作为异常管理
可以抛出和捕获exception 类或者子类的对象:以值传递抛出,以引用方式捕获;
在抛出异常的方法添加throw (抛出异常类型指针)
bool ftory::inRange()throw (const char *)
{}
try{
可以抛出异常的方法调用
}
catch (抛出的异常类型)//对应异常的catch (指定为 const char* 类型)
{
std::cout << e;//输出异常
}
例子
#include<exception>//异常头文件 没体现出来
#include <iostream>
#include<climits>//引用c限制头文件
class ftory
{
public:
ftory(unsigned short num);
~ftory();
unsigned long get_ftory();
bool inRange();
private:
unsigned short number;
};
ftory::ftory(unsigned short num)
{
this->number = num;
std::cout << "输入参数为" << this->number << '\n';
}
ftory::~ftory()
{
}
unsigned long ftory::get_ftory()
{
unsigned long sun = 1;
for (int i = 1; i <= number; i++)
{
sun *= i;
}
return sun;
}
bool ftory::inRange()throw (const char *)//能抛出异常的方法定义
{
unsigned long temp = ftory::get_ftory();
if (temp >= USHRT_MAX)
{//这是climit头文件里面的限制参数
throw "数值太大了 抛出了一个字符异常 ";//抛出异常 为const char *类型
return false; //超出限制就返回false
}
else
{
return true;
}
}
int main()
{
ftory a(20);
std::cout << a.get_ftory() << '\n';
try {//可能会抛出异常额函数
std::cout << a.inRange() << '\n';
}
catch (const char* e)//对应异常的catch (指定为 const char* 类型)
{
std::cout << e;
}
std::cout << "Hello World!\n";
}
34动态内存管理
静态内存,结构体、变量、数组 静态内存,固定占用固定大小的内存
动态内存:由一些没有名字、只有地址的内存块构成,那些内存块是在程序运行期间动态分配;
由new 申请内存返回地址;
1、如inte *p =new int;申请了int类型大小的内存,并且返回内存起始地址
2、没有足够的内存空间,new语句抛出异常std::bad_alloc异常;
3、用完内存之后要用delete释放内存;且将指针设置为null;没有释放就是bug;
4、new 出来的内存要先使用填充数据,再访问读取;或者构造器里面直接先初始化
关于这个p=null 使用这个p无法访问,否则访问就程序中止;
int *p =new int;
delete p;
p=null;
35动态数组
int a = 10;
int* p = new int[a];//建立动态数组
delete []p;//删除数组
delete []p;//删除数组 方括号+数组的指针
36从函数或者方法返回内存地址;
//返回动态分配的内存地址才有意义
返回局部变量的地址没有意义,因为局部在函数结束时候,自动释放所有内存
#include <iostream>
int* hanshu(int a)
{
int* p = new int[a]; //返回动态分配的内存地址才有意义
int z = 0;//
//return &z; 返回局部变量的地址没有意义,因为局部在函数结束时候,自动释放所有内存
return p;
}
int main()
{
int* k;
k = hanshu(10);
delete[]k;
std::cout << "Hello World!\n";
}
形参指针
函数指针
指针函数
这三个和c的都一样
37副本构造器的例子
#include <iostream>
class MyClass
{
public:
MyClass(int* p);
MyClass(MyClass& d);
~MyClass();
MyClass& operator=(const MyClass& d);
void print();
private:
int* prt;
};
MyClass::MyClass(int* p)
{
std::cout << "进入主构造器\n";
prt = p;
std::cout << "退出主构造器\n";
}
MyClass::MyClass(MyClass& d)//这是副构造器,相当于obj2 = obj1;没有重载的时候,进行了逐位赋值;
{
std::cout << "进入副构造器\n";
*this = d;
std::cout << "退出副构造器\n";
}
MyClass& MyClass::operator=(const MyClass& d)// 运算符重载 d为右边参数。
{
std::cout << "进入副构造器\n";
if (this != &d)//是两个对象
{
delete prt;//释放左边的指针
prt = new int;//给左边指针创建内存
*prt = *d.prt;//右边的值赋值给左边的值
}
else
{
std::cout << "赋值号两边为同个对象,不做处理! \n ";
}
std::cout << "退出副构造器\n";
return *this;
}
void MyClass::print()
{
std::cout << *prt << std::endl;//打印指针值
std::cout <<"地址是" << prt <<'\n' << std::endl;//加了一个地址输出,可以看到是不同的地址;
//说明指向了不同的空间;不用担心在释放对象1的时候,把对象2的也释放掉了
}
MyClass::~MyClass()
{
}
int main()
{
MyClass obj1(new int(1));//给指针赋值了指向1的地址
MyClass obj2(new int(2));//给指针赋值了指向2的地址
obj1.print();
obj2.print();
obj2 = obj1;//
obj1.print();//打印出来的值都一样
obj2.print();
std::cout << "Hello World!\n";
}
38、强制转换类型
const_cast<MyClass*>(value);//用来改变value的常量性
dynamic_cast<MyClass*>(value);//如果value的类型不是MyClass类或者子类的指针,这个操作符将返回NULL
reinterpret_cast<T>(value)//在不进行任何实质性的转换的情况下,把一种类型的指针解释为另一种类型的指针,或者把一个整形数解释为另一种整形数
static_cast<T>();//用来进行强制类型转换而不做任何运行时检查,老式强制类型转换操作的替代品
//dynamic_cast<MyClass*>(value);使用这个歌需要加null判断;否则会引起问题
39避免内存泄漏
1、动态申请内存,要删除
2、指针重复去指向第二个内存;释放的时候只能通过当前地址释放,所以第一个内存释放不了了;
在局部中申请动态内存就要在局部释放掉,或者返回这个动态内存的地址,在外部释放掉;(简单说,用了就要释放)
动态内存申请出来了,没有作用域;相当于全局的效果,但是指向指针有作用域;
40模块化编程
模块化编程
函数模块化;分成多个函数,方便编写调试,修改
文件模块化:方便功能模块移植
包含头文件在c++里面用尖尖的括号(系统级别的)或者双引号;一般头文件包含在一个专门包含头文件的文件中,这样方便引用;
头文件还可以定义宏,声明结构体、函数、模板和全局性常量等等,并且带有注释说明,函数和文件作用;
头文件使用
引用子目录下的fish.h;includes为子目录;当前目录下有includes子目录
includes/fish.h
同级别的子目录下引用fish.h;a目录下有includes和b文件夹,相对当前在b文件下
../includes/fish.h //..表上一级目录
1、本课程,就是为了展示c语言一样的.c和.h的使用;.cpp只用来代码定义,.h声明
2、防止因为多次引用头文件.h,防止编译器导入多次,防止重复声明、或者重复声明,然后报错;
使用如下:
#ifndef h文件名大写_H //包含一次头就判断一次 未定义过判断 ;定义过了就不会往下走了
#define h文件名大写_H //未定义就定义所有声明内容 //定义就被包含
所有声明内容
#endif//
命名空间
1、作用使得命名更多;
2、使能同一名字得函数不用重复:不同命名空间可以有相同函数或者类名字;
如果某个东西是在命名空间里面定义得,程序将不能立刻使用它。
这正是命名空间得意义:把东西放在小盒子里,不让他们与其他同名得发生冲突
访问方式如下::
1、
std::cout<<"1511";
2、using namespace std;
本文件自动使用命名空间;
cout<<"1511"; 等于std::cout<<"1511";,每个名都指定属于是std得空间
一般.c文件,生成一个命名空间kfc:盒子kfc
namespace kfc {//命名空间里面得定义
}一般.h文件,生成一个命名空间kfc:盒子kfc
namespace kfc {//命名空间里面的声明
}
例子如下
.h文件
#ifndef TEST_H
#define TEST_H
namespace ckf//命名空间里面的声明
{
void jisuan();
void print();
}
#endif // !1
.c文件
namespace kfc {//命名空间里面得定义
void jisuan()
{
}
void print()
{
}
}
#include <iostream>
#include"test.h"
int main()
{
kfc::jisuan();
std::cout << "Hello World!\n";
}
43链接和作用域
auto :作用域在局部,大括号里面的结束;存在栈里面,和局部一样,放在临时内存;
static 变量的声明周期一直保持下去,存储在静态存储区域;除非整个程序结束,否则不结束类似全局;但是这个变量
extern 外链接 告诉本单元,外部文件单元有一个这样的变量,可以在本单元使用;声明作用;
internal 内链接 只能本单元使用,比如static 就是内链接,指定局部使用(记得是局部不释放的)
none 无链接,局部变量就是(无static)
register 寄存器类型,CPU挂钩速度快,但是基本用不上
45函数模板
在函数重载的时候
要编写不同数据类型的实现函数如下
void swp(int *a,int *b)
void swp(double *a,double *b)
函数重载:虽然c++可以根据参数输入类型识别是用那个函数;但是两个函数都需要各自编写自己的实现代码;
此时函数模板就派上用场了,不需要编写大量的实现代码;
添加如下关键
template<class T>//这个class 不是一个类 是一种约定;固定形式;用来告诉编译器这个T在接下来的函数代表不确定数据
每一次方法的使用都要写 template <class T> 一次
接下来的T就可以代表任意类型
函数模板例子如下
#include <iostream>
template<class T>//这个class 不是一个类 是一种约定;固定形式;用来告诉编译器这个T在接下来的函数代表不确定数据
void swap(T &a,T &b)//T 指示了输入参数是不确定类型的,或者说任意的;这个也就是函数模板
{
T temp = a;
a = b;
b = temp;
}
int main()
{
int a = 1, b = 2;
std::string str = "1111", str2 = "222";
std::cout << "交换前:a b " << a << " " << b<<'\n';
swap( a, b);
std::cout << "交换后:a b " << a << " " << b << '\n';
std::cout << "交换前:str str2 " << str << " " << str2 << '\n';
swap(str,str2);
std::cout << "交换后:str str2 " << str << " " << str2 << '\n';
std::cout << "Hello World!\n";
}
在调用函数模板的时候可以指明类型:如下带有尖括号指定类型
swap<int>( a, b);
46类模板
类模板使得方法可以使得类可以使用模板,类的使得方法和成员可以使用任意类型;
PS
1、类声明和每一个方法定义之前,都要加一个template <class T>
2、只要用到类,如 类<T>, Stack< 指定T是什么类型>
类声明如下
template <class T>
class Stack
{
public:
Stack(unsigned int size);//构造器带参100
~Stack();
void push(T value);//参数是任意类型
void pod();
T pop();
private:
unsigned int size;
unsigned int sp;
T* data;//任意类型 T
};
类方法使用如下
template <class T>//使用一个T就模板一次
Stack<T>::Stack(unsigned int size )
{
this->size = size;
data = new T[size];//申请100个T类型的空间大小 ,这个T为任意类型
sp = 0;
}
void Stack<T>::push(T value)
{
data[sp++] = value;
}
使用类构造对象如下
Stack<int> intstack(100);//类指定T为int类型
程序例子
#include <iostream>
template <class T>
class Stack
{
public:
Stack(unsigned int size);//构造器带参100
~Stack();
void push(T value);//参数是任意类型
void pod();
T pop();
private:
unsigned int size;
unsigned int sp;
T* data;//任意类型 T
};
template <class T>//使用一个T就模板一次
Stack<T>::Stack(unsigned int size )
{
this->size = size;
data = new T[size];//申请100个T类型的空间大小 ,这个T为任意类型
sp = 0;
}
template <class T>
Stack<T>::~Stack()//析构器释放内存
{
delete[]data;//
}
template <class T>
void Stack<T>::push(T value)
{
data[sp++] = value;
}
template <class T>
T Stack<T>::pop()
{
return data[--sp];
}
int main()
{
Stack<int> intstack(100);//类指定T为int类型
intstack.push(1);
intstack.push(2);
intstack.push(3);
std::cout << intstack.pop() << '\n';
std::cout << intstack.pop() << '\n';
std::cout << intstack.pop() << '\n';
std::cout << "Hello World!\n";
}
47内联函数
编译器优化,内联就不起作用了
内敛函数作用:相当于宏,是编译器层面的,并不是函数调用那种有个函数地址,不会像函数那样被调用压栈;
内联函数形式如下
inline int add(参数 ,参数)
{
代码实现
}
编译时候:调用这个函数的地方,被如上代码替代;占用1k代码空间,每次调用编译都加1k;相当于宏一样的效果
类里面的内联函数
class person
{
person()
{//直接在类声明编写函数
this->name=name;
}
}
#include <iostream>
template <class T>
class Stack
{
public:
Stack(unsigned int size)//构造器带参100
{
this->size = size;
data = new T[size];//申请100个T类型的空间大小 ,这个T为任意类型
sp = 0;
}
~Stack()
{
delete[]data;//
}
void push(T value)//参数是任意类型{
{
data[sp++] = value;
}
T pop()
{
return data[--sp];
}
private:
unsigned int size;
unsigned int sp;
T* data;//任意类型 T
};
int main()
{
Stack<int> intstack(100);
intstack.push(1);
intstack.push(2);
intstack.push(3);
std::cout << intstack.pop() << '\n';
std::cout << intstack.pop() << '\n';
std::cout << intstack.pop() << '\n';
std::cout << "Hello World!\n";
}
48容器和算法
饶你国企
C处理处理数组的时候只能固定的数组;
c++提供了不定长数组,不用对数组长度做出限定,可以动态地增长或者添加数组长度
#include<vector> 需要包含头文件
声明容器形式 std::vector<类型> name;
#include <iostream>
#include<string>
#include<vector>
int main()
{
std::vector<std::string> name;
name.push_back("我是彭打工仔");// 名字就可以看出是压入块
name.push_back("我也是老板");// 名字就可以看出是压入块
// std::cout << name[0] << "\n";
for (int i = 0; i<name.size(); i++)
{
std::cout << name[0] << "\n";
}
name[0] = "123456789\n";
std::cout << name[0]<<'\n';//运行后从这里看出
std::cout << name[1] << '\n';//运行后从这里看出 这个name像是二维的
}
可以在第一维中直接赋值一串字符name[0] = "123456789\n";
49迭代器
、一种标准的访问容器方法;遍历复杂数据的指针;
每种容器都有一个迭代器,这只知道用就好了,都在c++容器封装好了;
迭代器是指向容器数据的指针
迭代器形式 std::vector<类型>::iterator iter //iter 是迭代器名字
std::vector<std::string>::iterator iter = name.begin();
迭代器使用例子
#include <iostream>
#include<string>
#include<vector>
int main()
{
std::vector<std::string> name;
name.push_back("我是彭打工仔");// 名字就可以看出是压入块
name.push_back("我也是老板");// 名字就可以看出是压入块
// std::cout << name[0] << "\n";
std::vector<std::string>::iterator iter = name.begin();//指向容器数据的开始
while (iter!=name.end())//不等于容器数据的末尾
{
std::cout << *iter<< '\n';
++iter;
}
//std::cout << name[0] << '\n';//运行后从这里看出
//std::cout << name[1] << '\n';//运行后从这里看出 这个name像是二维的
}
name.begin();//数据开始地址
name.end()//结束地址
算法
个人理解算法的定义:以优的方式得到高效结果
#include<algorithm>//c++提供算法在这个头文件
需要什么就去百度查,小鱼就介绍了个排序
#include <iostream>
#include<string>
#include<vector>
#include<algorithm>//c++提供算法在这个头文件
int main()
{
std::vector<std::string> name;
//name.push_back("我是彭打工仔");// 名字就可以看出是压入块
//name.push_back("我也是老板");// 名字就可以看出是压入块
// std::cout << name[0] << "\n";
name.push_back("58\n");// 名字就可以看出是压入块
name.push_back("18\n");// 名字就可以看出是压入块
name.push_back("98\n");// 名字就可以看出是压入块
name.push_back("38\n");// 名字就可以看出是压入块
std::sort(name.begin(), name.end());//首字母排序算法,参数1 容器数据首地址,容器数据尾地址
std::vector<std::string>::iterator iter = name.begin();//指向容器数据的开始
while (iter!=name.end())//不等于容器数据的末尾
{
std::cout << *iter<< '\n';
++iter;
}
//std::cout << name[0] << '\n';//运行后从这里看出
//std::cout << name[1] << '\n';//运行后从这里看出 这个name像是二维的
}