【C/C++】——c++特性
cppreference:https://zh.cppreference.com/w/cpp
C++基本语法
C++ 程序可以定义为对象的集合,这些对象通过调用彼此的方法进行交互。现在让我们简要地看一下什么是类、对象,方法、即时变量。
- 对象 - 对象具有状态和行为。例如:一只狗的状态 - 颜色、名称、品种,行为 - 摇动、叫唤、吃。对象是类的实例。
- 类 - 类可以定义为描述对象行为/状态的模板/蓝图。
- 方法 - 从基本上说,一个方法表示一种行为。一个类可以包含多个方法。可以在方法中写入逻辑、操作数据以及执行所有的动作。
- 即时变量 - 每个对象都有其独特的即时变量。对象的状态是由这些即时变量的值创建的。
C++类&对象
类定义
也就是说定义了:这个类的对象包括什么和可以在这个对象上执行那些操作
class Box
{
public:
double length; // 盒子的长度
double breadth; // 盒子的宽度
double height; // 盒子的高度
};
- 类定义是以关键字 class 开头,后跟类的名称。
- 关键字 public 确定了类成员的访问属性。
定义C++对象
//类名 对象名;
Box Box1; // 声明 Box1,类型为 Box
Box Box2; // 声明 Box2,类型为 Box
对象 Box1 和 Box2 都有它们各自的数据成员。
访问数据成员
类的对象的公共数据成员 可以使用(.)运算符来访问
#include <iostream>
using namespace std;
class Box
{
public:
double length; // 长度
double breadth; // 宽度
double height; // 高度
};
int main( )
{
Box Box1; // 声明 Box1,类型为 Box
Box Box2; // 声明 Box2,类型为 Box
double volume = 0.0; // 用于存储体积
// box 1 详述
Box1.height = 5.0;
Box1.length = 6.0;
Box1.breadth = 7.0;
// box 2 详述
Box2.height = 10.0;
Box2.length = 12.0;
Box2.breadth = 13.0;
// box 1 的体积
volume = Box1.height * Box1.length * Box1.breadth;
cout << "Box1 的体积:" << volume <<endl;
// box 2 的体积
volume = Box2.height * Box2.length * Box2.breadth;
cout << "Box2 的体积:" << volume <<endl;
return 0;
}
PS:私有的成员和受保护的成员 不能 使用直接成员访问运算符 (.) 来直接访问。
类 & 对象详解
概念 | 描述 |
---|---|
类成员函数 | 类的成员函数是指那些把定义和原型写在类定义内部的函数,就像类定义中的其他变量一样。 |
类访问修饰符 | 类成员可以被定义为 public、private 或 protected。默认情况下是定义为 private。 |
构造函数 & 析构函数 | 类的构造函数是一种特殊的函数,在创建一个新的对象时调用。类的析构函数也是一种特殊的函数,在删除所创建的对象时调用。 |
C++ 拷贝构造函数 | 拷贝构造函数,是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。 |
C++ 友元函数 | 友元函数可以访问类的 private 和 protected 成员。 |
C++ 内联函数 | 通过内联函数,编译器试图在调用函数的地方扩展函数体中的代码。 |
C++ 中的 this 指针 | 每个对象都有一个特殊的指针 this,它指向对象本身。 |
C++ 中指向类的指针 | 指向类的指针方式如同指向结构的指针。实际上,类可以看成是一个带有函数的结构。 |
C++ 类的静态成员 | 类的数据成员和函数成员都可以被声明为静态的。 |
类成员函数
可以定义在内部
class Box
{
public:
double length; // 长度
double breadth; // 宽度
double height; // 高度
double getVolume(void)
{
return length * breadth * height;
}
};
也可以定义在外部,用范围解析运算符 :: 定义该函数,须在类的你不声明函数
double Box::getVolume(void)
{
return length * breadth * height;
}
ps:在 :: 运算符之前必须使用类名。
类访问修饰符
PS:
- 关键字 public、private、protected 称为访问修饰符。
- 可以有多克关键字标记区域
class Base {
public:
// 公有成员
protected:
// 受保护成员
private:
// 私有成员
};
公有public成员
在类的外部时可以访问的
私有(private)成员
类的外部是不可访问的,甚至是不可查看的。只有类和友元函数可以访问私有成员。
保护(protected)成员
也是不可子啊类的外部访问,甚至不可以查看,但是不同的时,保护成员在派生类(即子类)中是可访问的。
#include <iostream>
using namespace std;
class Box
{
protected:
double width;
};
class SmallBox:Box // SmallBox 是派生类
{
public:
void setSmallWidth( double wid );
double getSmallWidth( void );
};
// 子类的成员函数
double SmallBox::getSmallWidth(void)
{
return width ;
}
void SmallBox::setSmallWidth( double wid )
{
width = wid;
}
// 程序的主函数
int main( )
{
SmallBox box;
// 使用成员函数设置宽度
box.setSmallWidth(5.0);
cout << "Width of box : "<< box.getSmallWidth() << endl;
return 0;
}
C++继承
- 继承允许我们依据另一个类来定义一个类
- 已有的类称为基类,新建的类称为派生类。
- 继承代表了 is a 关系。
基类 & 派生类
类派生列表来指定基类
class derived-class: access-specifier base-class
访问修饰符 access-specifier 是 public、protected 或 private 其中的一个
base-class 是之前定义过的某个类的名称。
如果未使用访问修饰符 access-specifier,则默认为 private。
#include <iostream>
using namespace std;
// 基类
class Shape
{
public:
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
// 派生类
class Rectangle: public Shape
{
public:
int getArea()
{
return (width * height);
}
};
int main(void)
{
Rectangle Rect;
Rect.setWidth(5);
Rect.setHeight(7);
// 输出对象的面积
cout << "Total area: " << Rect.getArea() << endl;
return 0;
}
访问控制和继承
基类成员如果不想被派生类的成员函数访问,则应在基类中声明为 private。
访问 | public | protected | private |
---|---|---|---|
同一个类 | yes | yes | yes |
派生类 | yes | yes | no |
外部的类 | yes | no | no |
一个派生类继承了所有的基类方法,但下列情况除外:
- 基类的构造函数、析构函数和拷贝构造函数。
- 基类的重载运算符。
- 基类的友元函数。
继承类型
- 基类可以被继承为 public、protected 或 private 几种类型
- 通常使用 public 继承
- 公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
- 保护继承(protected): 当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。
- 私有继承(private):当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。
多继承
一个子类有多个父类,它继承了多个父类的特性。
class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…
{
<派生类类体>
};
实例
#include <iostream>
using namespace std;
// 基类 Shape
class Shape
{
public:
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
// 基类 PaintCost
class PaintCost
{
public:
int getCost(int area)
{
return area * 70;
}
};
// 派生类
class Rectangle: public Shape, public PaintCost
{
public:
int getArea()
{
return (width * height);
}
};
int main(void)
{
Rectangle Rect;
int area;
Rect.setWidth(5);
Rect.setHeight(7);
area = Rect.getArea();
// 输出对象的面积
cout << "Total area: " << Rect.getArea() << endl;
// 输出总花费
cout << "Total paint cost: $" << Rect.getCost(area) << endl;
return 0;
}
C++ 重载运算符和重载函数
函数重载
可以声明功能类似的同名函数,函数名称相同,但是形参必须不停
#include <iostream>
using namespace std;
class printData
{
public:
void print(int i) {
cout << "整数为: " << i << endl;
}
void print(double f) {
cout << "浮点数为: " << f << endl;
}
void print(char c[]) {
cout << "字符串为: " << c << endl;
}
};
int main(void)
{
printData pd;
// 输出整数
pd.print(5);
// 输出浮点数
pd.print(500.263);
// 输出字符串
char c[] = "Hello C++";
pd.print(c);
return 0;
}
运算符重载
- 重定义或重载大部分 C++ 内置的运算符
- 重载的运算符是带有特殊名称的函数
#include <iostream>
using namespace std;
class Box
{
public:
double getVolume(void)
{
return length * breadth * height;
}
void setLength( double len )
{
length = len;
}
void setBreadth( double bre )
{
breadth = bre;
}
void setHeight( double hei )
{
height = hei;
}
// 重载 + 运算符,用于把两个 Box 对象相加
Box operator+(const Box& b)
{
Box box;
box.length = this->length + b.length;
box.breadth = this->breadth + b.breadth;
box.height = this->height + b.height;
return box;
}
private:
double length; // 长度
double breadth; // 宽度
double height; // 高度
};
// 程序的主函数
int main( )
{
Box Box1; // 声明 Box1,类型为 Box
Box Box2; // 声明 Box2,类型为 Box
Box Box3; // 声明 Box3,类型为 Box
double volume = 0.0; // 把体积存储在该变量中
// Box1 详述
Box1.setLength(6.0);
Box1.setBreadth(7.0);
Box1.setHeight(5.0);
// Box2 详述
Box2.setLength(12.0);
Box2.setBreadth(13.0);
Box2.setHeight(10.0);
// Box1 的体积
volume = Box1.getVolume();
cout << "Volume of Box1 : " << volume <<endl;
// Box2 的体积
volume = Box2.getVolume();
cout << "Volume of Box2 : " << volume <<endl;
// 把两个对象相加,得到 Box3
Box3 = Box1 + Box2;
// Box3 的体积
volume = Box3.getVolume();
cout << "Volume of Box3 : " << volume <<endl;
return 0;
}
可重载运算符/不可重载运算符
双目算术运算符 | + (加),-(减),*(乘),/(除),% (取模) |
---|---|
关系运算符 | ==(等于),!= (不等于),< (小于),> (大于>,<=(小于等于),>=(大于等于) |
逻辑运算符 | ||(逻辑或),&&(逻辑与),!(逻辑非) |
单目运算符 | + (正),-(负),*(指针),&(取地址) |
自增自减运算符 | ++(自增),--(自减) |
位运算符 | | (按位或),& (按位与),~(按位取反),^(按位异或),,<< (左移),>>(右移) |
赋值运算符 | =, +=, -=, *=, /= , % = , &=, |=, ^=, <<=, >>= |
空间申请与释放 | new, delete, new[ ] , delete[] |
其他运算符 | ()(函数调用),->(成员访问),,(逗号),[](下标) |
下面是不可重载的运算符列表:
- .:成员访问运算符
- .*, ->*:成员指针访问运算符
- :::域运算符
- sizeof:长度运算符
- ?::条件运算符
- #: 预处理符号
运算符重载实例
序号 | 运算符和实例 |
---|---|
1 | 一元运算符重载 |
2 | 二元运算符重载 |
3 | 关系运算符重载 |
4 | 输入/输出运算符重载 |
5 | ++ 和 -- 运算符重载 |
6 | 赋值运算符重载 |
7 | 函数调用运算符 () 重载 |
8 | 下标运算符 [] 重载 |
9 | 类成员访问运算符 -> 重载 |
C++多态
- 不同的类,都带有同一个名称但具有不同实现的函数,函数的参数甚至可以是相同的。
- C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。
执行以下
#include <iostream>
using namespace std;
class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
int area()
{
cout << "Parent class area :" <<endl;
return 0;
}
};
class Rectangle: public Shape{
public:
Rectangle( int a=0, int b=0):Shape(a, b) { }
int area ()
{
cout << "Rectangle class area :" <<endl;
return (width * height);
}
};
class Triangle: public Shape{
public:
Triangle( int a=0, int b=0):Shape(a, b) { }
int area ()
{
cout << "Triangle class area :" <<endl;
return (width * height / 2);
}
};
// 程序的主函数
int main( )
{
Shape *shape;
Rectangle rec(10,7);
Triangle tri(10,5);
// 存储矩形的地址
shape = &rec;
// 调用矩形的求面积函数 area
shape->area();
// 存储三角形的地址
shape = &tri;
// 调用三角形的求面积函数 area
shape->area();
return 0;
}
结果:
Parent class area
Parent class area
原因:
调用函数 area() 被编译器设置为基类中的版本,这就是所谓的静态多态,或静态链接 - 函数调用在程序执行前就准备好了
修改:
在 Shape 类中,area() 的声明前放置关键字 virtual
class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
virtual int area()
{
cout << "Parent class area :" <<endl;
return 0;
}
};
虚函数
- 虚函数 是在基类中使用关键字 virtual 声明的函数。也就是说,上面修改后的函数时虚函数
- 动态链接,或后期绑定
纯虚函数
适用于,在基类声明函数,但是没有主体,就叫纯虚函数
class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
// pure virtual function
virtual int area() = 0;
};
=== 0 告诉编译器,函数没有主体,上面的虚函数是纯虚函数。==
C++ 数据抽象
数据抽象:呈现必要的关键信息
在 C++ 中,我们使用类来定义我们自己的抽象数据类型(ADT)。您可以使用类 iostream 的 cout 对象来输出数据到标准输出
https://www.runoob.com/cplusplus/cpp-data-abstraction.html
C++数据封装
- 封装是面向对象编程中的把数据和操作数据的函数绑定在一起
- 数据封装是一种把数据和操作数据的函数捆绑在一起的机制
- 数据抽象是一种仅向用户暴露接口而把具体的实现细节隐藏起来的机制。
- 创建类来实现封装和数据隐藏
- 尽可能地对外隐藏每个类的实现细节。
#include <iostream>
using namespace std;
class Adder{
public:
// 构造函数
Adder(int i = 0)
{
total = i;
}
// 对外的接口
void addNum(int number)
{
total += number;
}
// 对外的接口
int getTotal()
{
return total;
};
private:
// 对外隐藏的数据
int total;
};
int main( )
{
Adder a;
a.addNum(10);
a.addNum(20);
a.addNum(30);
cout << "Total " << a.getTotal() <<endl;
return 0;
}
通常情况下,我们都会设置类成员状态为私有(private),除非我们真的需要将其暴露,这样才能保证良好的封装性。
这通常应用于数据成员,但它同样适用于所有成员,包括虚函数。
C++接口(抽象类)
接口描述了类的行为和功能
抽象类:类中至少有一个函数被声明为纯虚函数,则这个类就是抽象类。
纯虚函数是通过在声明中使用 "= 0" 来指定的
class Box
{
public:
// 纯虚函数
virtual double getVolume() = 0;
private:
double length; // 长度
double breadth; // 宽度
double height; // 高度
};
设计抽象类的目的是:为了给其他类提供一个可以继承的适当的基类。
抽象类不能被用于实例化对象,它只能作为接口使用。
抽象类:不能被用于实例化对象
具体类:可用于实例化对象的类
如下,时抽象类
#include <iostream>
using namespace std;
// 基类
class Shape
{
public:
// 提供接口框架的纯虚函数
virtual int getArea() = 0;
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
// 派生类
class Rectangle: public Shape
{
public:
int getArea()
{
return (width * height);
}
};
class Triangle: public Shape
{
public:
int getArea()
{
return (width * height)/2;
}
};
int main(void)
{
Rectangle Rect;
Triangle Tri;
Rect.setWidth(5);
Rect.setHeight(7);
// 输出对象的面积
cout << "Total Rectangle area: " << Rect.getArea() << endl;
Tri.setWidth(5);
Tri.setHeight(7);
// 输出对象的面积
cout << "Total Triangle area: " << Tri.getArea() << endl;
return 0;
}
这个架构也使得新的应用程序可以很容易地被添加到系统中,即使是在系统被定义之后依然可以如此。
C++文件&流
-
iostream 标准库
cin:标准输入读取流
cout:标准输出写入流
-
标准库 fstream :文件读取流和向文件写入流
数据类型 描述 ofstream 该数据类型表示输出文件流,用于创建文件并向文件写入信息。 ifstream 该数据类型表示输入文件流,用于从文件读取信息。 fstream 该数据类型通常表示文件流,且同时具有 ofstream 和 ifstream 两种功能,这意味着它可以创建文件,向文件写入信息,从文件读取信息。
打开文件
-
ofstream fstream 打开文件写操作
-
ifstream 读操作
-
open() 函数是 fstream、ifstream 和 ofstream 对象的一个成员。
void open(const char *filename, ios::openmode mode);
第一参数指定要打开的文件的名称和位置,第二个参数定义文件被打开的模式。
模式标志 描述 ios::app 追加模式。所有写入都追加到文件末尾。 ios::ate 文件打开后定位到文件末尾。 ios::in 打开文件用于读取。 ios::out 打开文件用于写入。 ios::trunc 如果该文件已经存在,其内容将在打开文件之前被截断,即把文件长度设为 0。
例子:
用写模式打开文件,并希望截断文件
ofstream outfile;
outfile.open("file.dat", ios::out | ios::trunc );
用于读写
ifstream afile;
afile.open("file.dat", ios::out | ios::in );
关闭文件
void close();
写入文件
- 像cout<<一样写入文件信息
- 不同的是,使用ofstream或fstream对象,而不是cout对象
读取文件
- 和cin<<一样读取键盘的输入的信息
- 这里您使用的是 ifstream 或 fstream 对象,而不是 cin 对象。
读取&写入实例
#include <fstream>
#include <iostream>
using namespace std;
int main ()
{
char data[100];
// 以写模式打开文件
ofstream outfile;
outfile.open("afile.dat");
cout << "Writing to the file" << endl;
cout << "Enter your name: ";
cin.getline(data, 100);
// 向文件写入用户输入的数据
outfile << data << endl;
cout << "Enter your age: ";
cin >> data;
cin.ignore();
// 再次向文件写入用户输入的数据
outfile << data << endl;
// 关闭打开的文件
outfile.close();
// 以读模式打开文件
ifstream infile;
infile.open("afile.dat");
cout << "Reading from the file" << endl;
infile >> data;
// 在屏幕上写入数据
cout << data << endl;
// 再次从文件读取数据,并显示它
infile >> data;
cout << data << endl;
// 关闭打开的文件
infile.close();
return 0;
}
文件位置指针
- istream 和 ostream 都提供了用于重新定位文件位置指针的成员函数。
- 关于 istream 的 seekg("seek get")和关于 ostream 的 seekp("seek put")
- seekg 和 seekp 的参数通常是一个长整型。第二个参数可以用于指定查找方向。
- 查找方向可以是 ios::beg(默认的,从流的开头开始定位),也可以是 ios::cur(从流的当前位置开始定位),也可以是 ios::end(从流的末尾开始定位)。
- 文件位置指针是一个整数值,指定了从文件的起始位置到指针所在位置的字节数。
// 定位到 fileObject 的第 n 个字节(假设是 ios::beg)
fileObject.seekg( n );
// 把文件的读指针从 fileObject 当前位置向后移 n 个字节
fileObject.seekg( n, ios::cur );
// 把文件的读指针从 fileObject 末尾往回移 n 个字节
fileObject.seekg( n, ios::end );
// 定位到 fileObject 的末尾
fileObject.seekg( 0, ios::end );
C++异常处理📆
C++命名空间📆
C++预处理器📆
C++动态内存
参考:https://blog.csdn.net/Code_beeps/article/details/89625473#comments
C++程序的内存分为
- 栈:在函数内部声明的所有变量都将占用栈内存
- 堆:这是程序中位使用的内存,用于动态分配内存
new 和 delete 运算符
new data-type;
double* pvalue = NULL; // 初始化为 null 的指针
pvalue = new double; // 为变量请求内存
检查 new 运算符是否返回 NULL 指针
double* pvalue = NULL;
if( !(pvalue = new double ))
{
cout << "Error: out of memory." <<endl;
exit(1);
}
释放内存
delete pvalue; // 释放 pvalue 所指向的内存
例子:
#include <iostream>
using namespace std;
int main ()
{
double* pvalue = NULL; // 初始化为 null 的指针
pvalue = new double; // 为变量请求内存
*pvalue = 29494.99; // 在分配的地址存储值
cout << "Value of pvalue : " << *pvalue << endl;
delete pvalue; // 释放内存
return 0;
}
数组的动态内存分配
char* pvalue = NULL; // 初始化为 null 的指针
pvalue = new char[20]; // 为变量请求内存
delete [] pvalue; // 删除 pvalue 所指向的数组
例子:
/*=====================一维数组==============*/
// 动态分配,数组长度为 m
int *array=new int [m];
//释放内存
delete [] array;
/*==============二维数组=======================*/
int **array
// 假定数组第一维长度为 m, 第二维长度为 n
// 动态分配空间
array = new int *[m];
for( int i=0; i<m; i++ )
{
array[i] = new int [n] ;
}
//释放
for( int i=0; i<m; i++ )
{
delete [] array[i];
}
delete [] array;
/*==================三维数组=======================*/
int ***array;
// 假定数组第一维为 m, 第二维为 n, 第三维为h
// 动态分配空间
array = new int **[m];
for( int i=0; i<m; i++ )
{
array[i] = new int *[n];
for( int j=0; j<n; j++ )
{
array[i][j] = new int [h];
}
}
//释放
for( int i=0; i<m; i++ )
{
for( int j=0; j<n; j++ )
{
delete[] array[i][j];
}
delete[] array[i];
}
delete[] array;
对象的动态内存分配
#include <iostream>
using namespace std;
class Box
{
public:
Box() {
cout << "调用构造函数!" <<endl;
}
~Box() {
cout << "调用析构函数!" <<endl;
}
};
int main( )
{
Box* myBoxArray = new Box[4];
delete [] myBoxArray; // 删除数组
return 0;
}
如果要为一个包含四个 Box 对象的数组分配内存,构造函数将被调用 4 次,同样地,当删除这些对象时,析构函数也将被调用相同的次数(4次)。
调用构造函数!
调用构造函数!
调用构造函数!
调用构造函数!
调用析构函数!
调用析构函数!
调用析构函数!
调用析构函数!
C++模板
参考:https://www.jianshu.com/p/8156b4aae3bd
- 模板是泛型编程的基础
- 泛型编程:以一种独立于任何特定类型的方式编写代码。
- 库容器,比如迭代器和算法,都是泛型编程
函数模板
template <typename type>
ret-type func-name(parameter list)
{
// 函数的主体
}
template <模板形参表>
<返回值类型> <函数名>(模板函数形参表)
{
//函数定义体
}
#include <iostream>
#include <string>
using namespace std;
template <typename T>
inline T const& Max (T const& a, T const& b)
{
return a < b ? b:a;
}
int main ()
{
int i = 39;
int j = 20;
cout << "Max(i, j): " << Max(i, j) << endl;
double f1 = 13.5;
double f2 = 20.7;
cout << "Max(f1, f2): " << Max(f1, f2) << endl;
string s1 = "Hello";
string s2 = "World";
cout << "Max(s1, s2): " << Max(s1, s2) << endl;
return 0;
}
类模板
template <class type> class class-name {
.
.
.
}
//type 是占位符类型名称
#include <iostream>
#include <vector>
#include <cstdlib>
#include <string>
#include <stdexcept>
using namespace std;
template <class T>
class Stack {
private:
vector<T> elems; // 元素
public:
void push(T const&); // 入栈
void pop(); // 出栈
T top() const; // 返回栈顶元素
bool empty() const{ // 如果为空则返回真。
return elems.empty();
}
};
/*===============成员函数实现===================*/
// 入栈
template <typename T>
void Stack<T>::push(T const& e)
{
elems.push_back(e);
}
// 出栈
template <typename T>
void Stack<T>::pop()
{
if (elems.empty()) {
throw std::out_of_range("out of range");
}
T e = elems.back();
elems.pop_back(); // 删除最后一个元素
return e;
}
// 返回栈顶原始
template <typename T>
T Stack<T>::top() const
{
if (elems.empty()) {
throw std::out_of_range("out of range");
}
return elems.back(); // 返回最后一个元素的拷贝
}
int main()
{
try {
Stack<int> intStack; // int 类型的栈
Stack<string> stringStack; // string 类型的栈
// 操作 int 类型的栈
intStack.push(7);
cout << intStack.top() <<endl;
// 操作 string 类型的栈
stringStack.push("hello");
cout << stringStack.top() << std::endl;
stringStack.pop();
stringStack.pop();
}
catch (exception const& ex) {
cerr << "Exception: " << ex.what() <<endl;
return -1;
}
}
- 类模板不代表一个具体的类,实际的类,而是代表一类类;
- 也就是说,类模板的使用呢,就是把类模板实例化一个具体的类
- 只有那些被调用的成员函数,才会产生这些函数的实例化代码。
- 如果类模板中含有静态成员,那么用来实例化的每种类型,都会实例化这些静态成员。
切记,要作为模板参数类型,唯一的要求就是:该类型必须提供被调用的所有操作
C++多线程📆
参考文章:
https://www.runoob.com/w3cnote/cpp-multithread-demo.html
https://zh.cppreference.com/w/cpp/thread/thread
值得一看:https://www.cnblogs.com/haippy/p/3284540.html
#include "stdafx.h"
#include <thread> //thread 头文件,实现了有关线程的类
#include <iostream>
void t1_run() {
// 线程1:循环1000次并输出"线程1正在运行"
for (int i = 0; i < 1000; i++)
{
std::cout << "线程1正在运行" << std::endl;
}
}
void t2_run() {
// 线程2:循环1000次并输出"线程2正在运行"
for (int i = 0; i < 1000; i++)
{
std::cout << "线程2正在运行" << std::endl;
}
}
int main()
{
// 创建两个线程对象,将要运行的函数作为参数
std::thread t1(t1_run);
std::thread t2(t2_run);
// join()函数,运行线程.
t1.join();
t2.join();
return 0;
}
#include<iostream>
#include<string>
#incldue<thread>
using namespace std;
int main(const int argc,const char** argv)
{
}
Output: 两个线程交替运行
总结:
Step 1: 引入thread头文件
Step 2:编写一个或多个函数(void返回值)
Step 3:创建线程对象,std::thread()
Step 4:运行线程,join()
互斥算法
互斥算法避免多个线程同时访问共享资源。
void test()
{
m.lock();//锁定:获取互斥锁
cout<<"线程持有互斥锁\n";
/*
同步代码:
只能同时被一个线程运行的代码
*/
this_thread::sleep_for(2s);
cout<<"线程即将释放互斥锁\n";
m.unlock(); //解锁,释放互斥锁
}
int main(const int argc,const char** argv)
{
thread(&test).detach(); //开辟线程
this_thread::sleep_for(500ms);
m.lock();
cout<<"主线程持有互斥锁\n";
/*
同步代码:
只能同时被一个线程运行的代码
*/
this_thread::sleep_for(1s);
cout<<"主线程即将释放互斥锁\n";
m.unlock();
}
if(m.try_lock())//尝试获取互斥锁
{
cout<<"主线程持有互斥锁\n";
/*
同步代码:
只能同时被一个线程运行的代码
*/
this_thread::sleep_for(1s);
cout<<"主线程即将释放互斥锁\n";
m.unlock();
}