c++笔记2

 小甲鱼学习课程

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子类的构造器和析构

创建对象时:

先执行父类的构造器

才到子类的构造器;

销毁对象时:

子类的最后一条语句结束后调用基类的析构器;

  1. 首先调用子类的析构器。
  2. 然后调用父类的析构器。

带参数的构造器声明

父类,基类

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()

C++学习笔记-优快云博客

参考其中的章节: 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错误处理和调试

错误分为编译错误和运行错误

编译时错误

经验:

  1. 在编程的时候保持一种风格、一旦决定要如何命名变量和函数、如何缩进、何种格式编写代码;
  2.   认真对待编译器的错误和警告
  3. 先画流程图
  4. 编译错误、先完整的看一遍代码、再矫正错误;
  5. 注意基本语法
  6. 把可能有问题的代码注释掉
  7. 换一个环境或者开发工具:可能是杀毒软件,什么之类的影响;
  8. 头文件包含
  9. 留意变量的作用域和命名空间
  10. 休息一下:效率下降后需要休息;
  11. 多使用调试工具
  12. 把调试好的程序控制保存起来;把代码划分成模块,来搭建新程序可以减少很多时间;

运行时的错误

逻辑漏洞;bug同一个产品一个好一个坏;时有时无没法确定;

经验;

  1. 保持一种良好的编程风格
  2. 多注释,注释好,特别是参数变化;简单代码,不要啰嗦
  3. 操作符优先级,括号很保险
  4. 输入输出合法性检查
  5. 不要做任何假设:用户不会按照你的想法去使用你的程序;
  6. 划分单元模块,这样测试方便

让函数返回错误代码

输入输出错误码

使用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

捕获异常

运行错误分为两种

  1. 开发 调试 测试过程中,尽可能的查找和定位错误;使用函数assert等等;
  2. 和最终用户有关,尽可能的把用户感受放第一位;用户不懂技术,要用户能轻松使用;输出也不能全部开放,看不懂只会增加用户麻烦;

大公司微软都不可能没有bug,关键在于后续升级

捕获异常:

为了对付潜在的编程错误。异常:就是与期望不相符合的反常现象

捕获异常的的基本使用思路

  1. 安排c++代码去尝试某件事 (try 语句)
  2. 如果发生问题就抛出一个异常
  3. 再安排一些代码捕获这个异常并进行相应的处理(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像是二维的
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值