首页 > 编程语言 >C++高级编程

C++高级编程

时间:2023-06-02 11:36:43浏览次数:34  
标签:std main int 编程 namespace 高级 C++ 线程 include



文章目录

  • C++文件和流
  • 打开文件
  • 关闭文件
  • 写入文件
  • 读取文件
  • 读取&写入实例
  • 文件位置指针
  • C++异常处理
  • 抛出异常
  • 捕获异常
  • C++标准的异常
  • 定义新的异常
  • 动态内存
  • new和delete运算符
  • 数组的动态内存分配
  • 对象的动态内存分配
  • C++命名空间
  • 定义命名空间
  • using指令
  • 不连续的命名空间
  • 嵌套的命名空间
  • 关于命名空间内变量和函数及全局变量的使用和作用域
  • C++模版
  • 函数模版
  • 类模板
  • C++预处理器
  • define预处理
  • 参数宏
  • 条件编译
  • "#和##运算符"
  • C++中的预定义宏
  • C++信号处理
  • signal()函数
  • raise()函数
  • Sleep函数
  • Linux 用 #include


C++文件和流

数据类型

描述

ofstream

该数据类型标示输出文件流,用于创建文件并向文件写入信息

ifstream

该数据类型标示输入文件流,用于从文件读取信息

fstream

该数据类型通常标示文件流,且同时具有ofstream和ifstream两种功能,着意味着它可以创建文件,向文件写入信息,从文件读取信息

打开文件

void open(const char *filename, ios::openmode mode);

模式标志

描述

ios::app

追加模式。所有写入都追加到文件末尾

ios::ate

文件打开后定位到文件末尾

ios::in

打开文件用于读取

ios::out

打开文件用于写入

ios::trunc

如果该文件已经存在,其内容将在打开文件之前被截断,即把文件长度设为0

ofstream outfile;
outfile.open("file.date", ios::out || ios::trunc);
ifstrea afile;
afile.open("file.dat", ios::out | ios::in);

关闭文件

void close();

写入文件

  • <<

读取文件

*>>

读取&写入实例

#include <iostream>
#include <fstream>
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;
}
$./a.out
Writing to the file
Enter your name: Zara
Enter your age: 9
Reading from the file
Zara
9

文件位置指针

// 定位到 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++ 异常处理涉及到三个关键字:try、catch、throw。

  • throw: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。
  • catch: 在您想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常。
  • try: try 块中的代码标识将被激活的特定异常。它后面通常跟着一个或多个 catch 块。

如果有一个块抛出一个异常,捕获异常的方法会使用 try 和 catch 关键字。try 块中放置可能抛出异常的代码,try 块中的代码被称为保护代码。使用 try/catch 语句的语法如下所示:

try
{
   // 保护代码
}catch( ExceptionName e1 )
{
   // catch 块
}catch( ExceptionName e2 )
{
   // catch 块
}catch( ExceptionName eN )
{
   // catch 块
}

抛出异常

double division(int a, int b)
{
	if(b == 0)
	{
		throw "Division by zero condition!";
	}
	return (a/b);
}

捕获异常

try
{
   // 保护代码
}catch( ExceptionName e )
{
  // 处理 ExceptionName 异常的代码
}


try
{
   // 保护代码
}catch(...)
{
  // 能处理任何异常的代码
}
#include <iostream>
#include <fstream>
using namespace std;
double division(int a, int b)
{
    if(b == 0)
    {
        throw "Division by zero condition!";
    } 
    return (a/b);
}   
int main()
{
    int x = 50;
    int y = 0;
    double z = 0;
    try{
        z = division(x,y);
        cout<<z<<endl;
    } catch(const char * msg){
        cerr<<msg<<endl;
    }
    return 0;
}
Division by zero condition!

C++标准的异常

C++高级编程_命名空间

异常

描述

std::exception

该异常是所有标准 C++ 异常的父类。

std::bad_alloc

该异常可以通过 new 抛出。

std::bad_cast

该异常可以通过 dynamic_cast 抛出。

std::bad_exception

这在处理 C++ 程序中无法预期的异常时非常有用。

std::bad_typeid

该异常可以通过 typeid 抛出。

std::logic_error

理论上可以通过读取代码来检测到的异常。

std::domain_error

当使用了一个无效的数学域时,会抛出该异常。

std::invalid_argument

当使用了无效的参数时,会抛出该异常。

std::length_error

当创建了太长的 std::string 时,会抛出该异常。

std::out_of_range

该异常可以通过方法抛出,例如 std::vector 和 std::bitset<>::operator。

std::runtime_error

理论上不可以通过读取代码来检测到的异常。

std::overflow_error

当发生数学上溢时,会抛出该异常。

std::range_error

当尝试存储超出范围的值时,会抛出该异常。

std::underflow_error

当发生数学下溢时,会抛出该异常。

定义新的异常

#include <iostream>
#include <fstream>
using namespace std;
struct MyException:public exception
{
    const char * what() const throw()
    {
        return "C++ Exception";
    }
};

int main()
{
    try
    {
        throw MyException();
    }
    catch(MyException& e)
    {
        std::cout<<"MyException caught"<<std::endl;
        std::cout<<e.whate()<<std::endl;
    }
    catch(std::exception& e)
    {
        //其他的错误
    }
    
}
MyException caught
C++ Exception

动态内存

解动态内存在 C++ 中是如何工作的是成为一名合格的 C++ 程序员必不可少的。C++ 程序中的内存分为两个部分:

  • 栈:在函数内部声明的所有变量都将占用栈内存。
  • 堆:这是程序中未使用的内存,在程序运行时可用于动态分配内存。
    很多时候,您无法提前预知需要多少内存来存储某个定义变量中的特定信息,所需内存的大小需要在运行时才能确定。

在 C++ 中,您可以使用特殊的运算符为给定类型的变量在运行时分配堆内的内存,这会返回所分配的空间地址。这种运算符即 new 运算符。

如果您不再需要动态分配的内存空间,可以使用 delete 运算符,删除之前由 new 运算符分配的内存。

new和delete运算符

double *pvalue = NULL; //初始化null的指针
pvalue = new double; //为变量请求内存
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 pavlue:"<<*pvalue<<endl;

	delete pvalue; //释放内存
	return 0;
}
Value of pvalue : 29495

数组的动态内存分配

char *pvalue = NULL; //初始化null的指针
pvalue = new char[20]; //为变量请求内存
delete [] pvalue; //删除pvalue所指向的数组
  • 一位数组
//动态分配,数组长度为m
int *array = new int[m];
//释放内存
delete [] array;
  • 二维数组
int **array
//假定数组第一维为名,第二维长度为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;
#include <iostream>
using namespace std;
int main()
{
    int **p;
    int i,j;
    //开始分配4行8列的二维数据
    p = new int*[4];
    for(i=0;i<4;i++) {
        p[i]=new int[8];
    }
    for(i=0; i<4; i++){
        for(j=0; j<8; j++) {
            p[i][j] = j*i;
        }
    }
    //打印数据
    for(i=0; i<4; i++) {
        for(j=0;j<8;j++){
            if(j==0) cout<<endl;
            cout<<p[i][j]<<"\t";
        }
    }
    //开始释放申请的堆
    for(i=0; i<4; i++) {
        delete[] p[i];
    }
    delete[] p;
    return 0;
}
  • 三位数组
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;
 
int main()
{   
    int i,j,k;   // p[2][3][4]
    
    int ***p;
    p = new int **[2]; 
    for(i=0; i<2; i++) 
    { 
        p[i]=new int *[3]; 
        for(j=0; j<3; j++) 
            p[i][j]=new int[4]; 
    }
    
    //输出 p[i][j][k] 三维数据
    for(i=0; i<2; i++)   
    {
        for(j=0; j<3; j++)   
        { 
            for(k=0;k<4;k++)
            { 
                p[i][j][k]=i+j+k;
                cout<<p[i][j][k]<<" ";
            }
            cout<<endl;
        }
        cout<<endl;
    }
    
    // 释放内存
    for(i=0; i<2; i++) 
    {
        for(j=0; j<3; j++) 
        {   
            delete [] p[i][j];   
        }   
    }       
    for(i=0; i<2; i++)   
    {       
        delete [] p[i];   
    }   
    delete [] p;  
    return 0;
}

对象的动态内存分配

#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;
}
调用构造函数!
调用构造函数!
调用构造函数!
调用构造函数!
调用析构函数!
调用析构函数!
调用析构函数!
调用析构函数!

C++命名空间

C++高级编程_命名空间_02

定义命名空间

namespace namespace_name{
	//代码声明
}
name::code; //code可以是变量或函数
#include <iostream>
using namespace std;

//第一个命名空间
namespace first_space{
	void func(){
		cout<<"Inside first_space"<<endl;
	}
}

namespace second_space{
	void func(){
		cout<<"Inside second_space"<<endl;
	}
}

int main()
{
	//调用第一个命名空间中的函数
	first_sapce::func();
	//调用第二个命名空间中的函数
	second_space::func();
	return 0;
}
Inside first_space
Inside second_space
在这里插入代码片

using指令

#include <iostream>
using namespace std;
 
// 第一个命名空间
namespace first_space{
   void func(){
      cout << "Inside first_space" << endl;
   }
}
// 第二个命名空间
namespace second_space{
   void func(){
      cout << "Inside second_space" << endl;
   }
}
using namespace first_space;
int main ()
{
 
   // 调用第一个命名空间中的函数
   func();
   
   return 0;
}
Inside first_space

也可以单独引用

using std::cout
#include <iostream>
using std::cout;
 
int main ()
{
 
   cout << "std::endl is used with std!" << std::endl;
   
   return 0;
}
std::endl is used with std!

不连续的命名空间

命名空间可以定义在几个不同的部分中,因此命名空间是由几个单独定义的部分组成的。一个命名空间的各个组成部分可以分散在多个文件中。

所以,如果命名空间中的某个组成部分需要请求定义在另一个文件中的名称,则仍然需要声明该名称。下面的命名空间定义可以是定义一个新的命名空间,也可以是为已有的命名空间增加新的元素:

namespace namespace_name {
   // 代码声明
}

嵌套的命名空间

namespace namespace_name1 {
   // 代码声明
   namespace namespace_name2 {
      // 代码声明
   }
}
// 访问 namespace_name2 中的成员
using namespace namespace_name1::namespace_name2;
 
// 访问 namespace:name1 中的成员
using namespace namespace_name1;
#include <iostream>
using namespace std;

//第一个命名空间
namespace first_space{
    void func(){
        cout << "Inside first_space"<<endl;
    }
    //第二个命名空间
    namespace second_space{
        void func(){
            cout<<"Inside second_space"<<endl;
        }
    }
}
using namespace first_space::second_space;
int main()
{
    //调用第二个命名空间中的函数
    func();
    return 0;
}
Inside second_space

关于命名空间内变量和函数及全局变量的使用和作用域

#include <iostream>
using namespace std;

namespace A
{
    int a = 100;
    namespace B //嵌套一个命名空间B
    {
        int a = 20;
    }
}
int a = 200;//定义一个全局变量
int main()
{
    cout<<"A::a"<<A::a<<endl;
    cout<<"A::B::a"<<A::B::a<<endl;
    cout<<"a="<<a<<endl;
    cout<<"::a="<<::a<<endl;

    int a = 30;
    cout<<"a="<<a<<endl;
    cout<<"::a"<<::a<<endl;
    return 0;
}
A::a =100  
A::B::a =20
a =200      //全局变量a
::a =200
a =30       //局部变量a
::a =200

即:全局变量 a 表达为 ::a,用于当有同名的局部变量时来区别两者。

错误示范

#include <iostream>
using namespace std;
namespace A
{
    int a = 100;
    int fun()
    {
        cout<<"a = "<<a<<endl;
    }

    namespace B            //嵌套一个命名空间B
    {
        int a =20;
        int fun()
        {
             cout<<"a = "<<a<<endl;
        }

    }
}


int main(int argc, char *argv[])
{
    cout<<a<<endl;
    fun();

    return 0;
}

这样会出错:会显示 a 变量和 fun 函数 “was not declared in this scope”,即找不到这个 a 和 fun 函数。

解决办法: 用 using 来告诉编译器用到的是哪个命名空间内的内容。在 main() 上面加 using namespace A; 或者 using namespace A::B; 。这样就可以使用其中的 a 和 fun()。但是不能同时使用,因为这样也会导致编译出错,编译器器不知道要去使用哪个 a 和 fun()。

C++模版

模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。

模板是创建泛型类或函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念。

每个容器都有一个单一的定义,比如 向量,我们可以定义许多不同类型的向量,比如 vector 或 vector 。

您可以使用模板来定义函数和类,接下来让我们一起来看看如何使用。

函数模版

template <class type>> ret-type func-name(parameter list)
{
	//函数的主体
}
#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;
}
Max(i, j): 39
Max(f1, f2): 20.7
Max(s1, s2): World

类模板

template <class type> class class-name {
.
.
.
}
#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 <class T>
void Stack<T>::push(T const& elem)
{
    elems.push_back(elem);
}

template <class T>
void Stack<T>::pop()
{
    if(elems.empty) {
        throw out_of_range("Stack<>::pop():empty stack");
    }
    elems.pop_back();
}

template <class T>
T Stack<T>::top() const
{
    if(elems.empty()) {
        throw out_of_range("Stack<>::top():empty stack");
    }

    return elems.back();
}

int main() {
    try{
        Stack<int> intStack;
        Stack<string> stringStack;

        intStack.push(7);
        cout<<intStack.top()<<endl;

        stringStack.push("hello");
        cout<<stringStack.top()<<std::endl;
        stringStack.pop();
        stringStack.pop();
    }
    catch (exception const& ex)
    {
        cerr<<"Exception:"<<ex.what()<<endl;
        return -1;
    }
    return 0;
}
7
hello
Exception: Stack<>::pop(): empty stack

C++预处理器

define预处理

#define macro-name replacement-text
#include <iostream>
using namespace std;
#define PI 3.14159

int main()
{
	cout<<"Value of PI:"<<PI<<endl;
	return 0;
}
$gcc - E test.cpp > test.p

...
int main()
{
	cout<<"Value of PI:"<<3.14159<<endl;
	return 0;
}

参数宏

#include <iostream>
using namespace std;

#define MIN(a,b) (a<b?a:b)

int main() 
{
	int i,j;
	i = 100;
	j = 30;
	cout<<"较小的值为:"<<MIN(i,j)<<endl;
	return 0;
}
较小的值为:30

条件编译

#ifdef NULL
	#define NULL 0
#endif
#ifdef DEBUG
	cerr<<"Variable x = "<<x<<endl;
#endif
#if 0 
	不进行编译的代码
#endif
#include <iostream>
using namesapce std;
#define DEBUG

#define MIN(a,b) (((a)<(b)) ? a : b)

int main()
{
	int i,j;
	i = 100;
	j = 30;
#ifdef DEBUG
	cerr << "Trace:Inside main funciton"<<endl;
#endif

#if 0
	/*这是注释部分*/
	cout<<MKSTR(HELLO C++)<<endl;
#endif
	
	cout<<"The minimu is"<<MIN(i,j)<<endl;

#ifdef DEBUG
	cerr<<"Trace:Coming out of main function"<<endl;
#endif
	return 0;
}
Trace: Inside main function
The minimum is 30
Trace: Coming out of main function

“#和##运算符”

# 和 ## 预处理运算符在 C++ 和 ANSI/ISO C 中都是可用的。# 运算符会把 replacement-text 令牌转换为用引号引起来的字符串。

#include <iostream>
using namespace std;
#define MKSTR(x) #x
int main()
{
	cout<<MKSTR(HELLO C++)<<endl;
}
HELLO C++
cout << MKSTR(HELLO C++) << endl;

转换成
cout << "HELLO C++" << endl;

## 运算符用于连接两个令牌。下面是一个实例:

#define CONCAT(x,y) x ## y
#include <iostream>
using namespace std;
#define concat(a,b) a ## b
int main()
{
	int xy = 100;
	cout<<concat(x,y);
	return 0;
}
cout<<concat(x,y)
转换成
cout<<xy;

C++中的预定义宏


描述

LINE

这会在程序编译时包含当前行号。

FILE

这会在程序编译时包含当前文件名

DATE

这会包含一个形式为month/day/year的字符串,它标示把源文件转换为目标代码的日期

TIME

这会包含一个形式为hour:minute:second的字符串,它标示程序被编译的时间。

#include <iostream>
using namespace std;
int main()
{
	cout<<"Value of __LINE__"<<__LINE__<<endl;
	cout<<"Value of __FILE__"<<__FILE__<<endl;
	cout<<"Value of __DATE__"<<__DATE__<<endl;
	cout<<"Value of __TIME__"<<__TIME__<<endl;
	return 0;
}
# 和 ## 运算符

# 字符串化的意思,出现在宏定义中的#是把跟在后面的参数转换成一个字符串。

当用作字符串化操作时,# 的主要作用是将宏参数不经扩展地转换成字符串常量。

 宏定义参数的左右两边的空格会被忽略,参数的各个 Token 之间的多个空格会被转换成一个空格。
 宏定义参数中含有需要特殊含义字符如"或\时,它们前面会自动被加上转义字符 \。
## 连接符号,把参数连在一起。

将多个 Token 连接成一个 Token。要点:

 它不能是宏定义中的第一个或最后一个 Token。
 前后的空格可有可无。

C++信号处理

这些信号是定义在 C++ 头文件 中。

信号

描述

SIGABRT

程序的异常终止,如调用 abort。

SIGFPE

错误的算术运算,比如除以零或导致溢出的操作。

SIGILL

检测非法指令。

SIGINT

接收到交互注意信号。

SIGSEGV

非法访问内存。

SIGTERM

发送到程序的终止请求。

signal()函数

void (*signal(int sig, void (*func)(int)))(int);

这个函数接收两个参数:第一个参数是一个整数,代表了信号的编号;第二个参数是一个指向信号处理函数的指针。

让我们编写一个简单的 C++ 程序,使用 signal() 函数捕获 SIGINT 信号。不管您想在程序中捕获什么信号,您都必须使用 signal 函数来注册信号,并将其与信号处理程序相关联。看看下面的实例:

#include <iostream>
#include <csignal>
#include <unistd.h>

using namespace std;
void signalHandler(int signum)
{
	cout << "Interrupt signal ("<<signum<<"| received.\n";
	//清理并关闭
	//终止程序
	exit(signum);
}

int main()
{
	//注册信号SIGINT和信号处理程序
	signal(SIGINT, signalHandler);
	while(1)
	{	
		cout<<"Going to sleep..."<<endl;
		sleep(1);
	}
	return 0;
}
Going to sleep....
Going to sleep....
Going to sleep....

使用Ctrl+C来中断程序,程序打印如下内容并退出:

Going to sleep....
Going to sleep....
Going to sleep....
Interrupt signal (2) received.

raise()函数

int raise(signal sig);

在这里,sig 是要发送的信号的编号,这些信号包括:SIGINT、SIGABRT、SIGFPE、SIGILL、SIGSEGV、SIGTERM、SIGHUP。以下是我们使用 raise() 函数内部生成信号的实例:

#include <iostream>
#include <csignal>
#include <unistd.h>

using namespace std;
void signalHandler(int signum)
{
	cout << "Interrupt signal ("<<signum<<") received.\n";
	//清理并关闭
	//终止程序
	exit(signum);
}
int main()
{
	int i=0;
	//注册信号SIGINT和信号处理程序
	signal(SIGINT, signalHandler);
	while(++i)
	{
		cout<<"Going to sleep"<<endl;
		if(i==3) {
			raise(SIGITNT);
		}
		sleep(1);
	}
	return 0;
}
Going to sleep....
Going to sleep....
Going to sleep....
Interrupt signal (2) received.

Sleep函数

功能:执行挂起一段时间,也就是等待一段时间在继续执行
用法:Sleep(时间)
注意:
(1)Sleep是区分大小写的,有的编译器是大写,有的是小写
(2)Sleep括号里的时间,在Windows下是以毫秒为单位,而Linux是以秒为单位。

#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
	int a = 1;
	while(a)
	{
		cout<<""欢迎来到菜鸟教程!<<endl;
		Sleep(100);
	}
	system("pause");
	return 0;
}

Linux 用 #include 和 sleep(),Windos 用 #include 和 Sleep()。

#include <iostream>
#include <csignal>
#include <windows.h>

void signalHandler(int signum)
{
    cout << "Interrupt signal (" << signum << ") received.\n";

    // 清理并关闭
    // 终止程序  

    exit(signum);

}

int main()
{
    int i = 0;
    // 注册信号 SIGINT 和信号处理程序
    signal(SIGINT, signalHandler);

    while (++i) {
        cout << "Going to sleep...." << endl;
        if (i == 3) {
            raise(SIGINT);
        }
        Sleep(1);
    }

    return 0;
}

C++多线程

多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序。一般情况下,两种类型的多任务处理:基于进程和基于线程。

  • 基于进程的多任务处理是程序的并发执行。
  • 基于线程的多任务处理是同一程序的片段的并发执行。
    多线程程序包含可以同时运行的两个或多个部分。这样的程序中的每个部分称为一个线程,每个线程定义了一个单独的执行路径。

本教程假设您使用的是Linux系统,我们要使用POSIX编程多线程C++程序。POSIX Threads或Pthreads提供的API可在多种类Unix-POSIX系统上可用,比如FreeBSD、NetBSD、GNU/Linux、Mac OS X和Solairs。

创建线程

创建一个POSIX线程

#include <pthread.h>
pthread_create(thread, attr, start_routine, arg)

pthread_create创建一个新的线程,并让它执行。

参数

描述

thread

指向线程标识符指针

attr

一个不透明的属性对象,可以被用来设置线程属性。您可以制定线程属性对象,也可以使用默认值NULL。

start_routine

线程运行函数起止地址,一旦线程被创建就会执行

arg

运行函数的参数。它必须通过把引用作为指针强制转换为void类型进行传递。如果没有传递参数,则使用NULL。

创建线程成功时,函数返回 0,若返回值不为 0 则说明创建线程失败。

终止线程

终止一个POSIX线程

#include <pthread.h>
pthread_exit(status)

在这里,pthread_exit 用于显式地退出一个线程。通常情况下,pthread_exit() 函数是在线程完成工作后无需继续存在时被调用。

如果 main() 是在它所创建的线程之前结束,并通过 pthread_exit() 退出,那么其他线程将继续执行。否则,它们将在 main() 结束时自动被终止。

实例

#include <iostream>
//必须的头文件
#include <pthread.h>
#define namespace std;

//线程的运行函数
void* say_hello(void* args)
{
    cout<<"Hellow Runoob!"<<endl;
    return 0;
}

int main()
{
    //定义线程的id变量,多个变量使用数组
    pthread_t tids[NUM_THREADS];
    for(int i=0; i<NUM_THREADS; ++i)
    {
        //参数依次是,创建的线程id,线程参数,调用的函数,传入的函数参数
        int ret = pthread_create(&tids[i], NULL, say_hello, NULL);
        if(ret != 0)
        {
            cout<<"pthread_create error: error_code"<<ret<<endl;
        }
    }
    //等各个线程退出后,进程才结束,否则进程强制结束了,线程可能还没反应过来
    pthread_exit(NULL);
}
$g++ test.cpp -lpthread -o test.o
$ ./test.o
Hello Runoob!
Hello Runoob!
Hello Runoob!
Hello Runoob!
Hello Runoob!

以下简单的实例代码使用 pthread_create() 函数创建了 5 个线程,并接收传入的参数。每个线程打印一个 “Hello Runoob!” 消息,并输出接收的参数,然后调用 pthread_exit() 终止线程。

//文件名:test.cpp
 
#include <iostream>
#include <cstdlib>
#include <pthread.h>
 
using namespace std;
 
#define NUM_THREADS     5
 
void *PrintHello(void *threadid)
{  
   // 对传入的参数进行强制类型转换,由无类型指针变为整形数指针,然后再读取
   int tid = *((int*)threadid);
   cout << "Hello Runoob! 线程 ID, " << tid << endl;
   pthread_exit(NULL);
}
 
int main ()
{
   pthread_t threads[NUM_THREADS];
   int indexes[NUM_THREADS];// 用数组来保存i的值
   int rc;
   int i;
   for( i=0; i < NUM_THREADS; i++ ){      
      cout << "main() : 创建线程, " << i << endl;
      indexes[i] = i; //先保存i的值
      // 传入的时候必须强制转换为void* 类型,即无类型指针        
      rc = pthread_create(&threads[i], NULL, 
                          PrintHello, (void *)&(indexes[i]));
      if (rc){
         cout << "Error:无法创建线程," << rc << endl;
         exit(-1);
      }
   }
   pthread_exit(NULL);
}
$ g++ test.cpp -lpthread -o test.o
$ ./test.o
main() : 创建线程, 0
main() : 创建线程, 1
Hello Runoob! 线程 ID, 0
main() : 创建线程, Hello Runoob! 线程 ID, 21

main() : 创建线程, 3
Hello Runoob! 线程 ID, 2
main() : 创建线程, 4
Hello Runoob! 线程 ID, 3
Hello Runoob! 线程 ID, 4

向线程传递参数

#include <iostream>
#include <cstdlib>
#include <pthread.h>
 
using namespace std;
 
#define NUM_THREADS     5
 
struct thread_data{
   int  thread_id;
   char *message;
};
 
void *PrintHello(void *threadarg)
{
   struct thread_data *my_data;
 
   my_data = (struct thread_data *) threadarg;
 
   cout << "Thread ID : " << my_data->thread_id ;
   cout << " Message : " << my_data->message << endl;
 
   pthread_exit(NULL);
}
 
int main ()
{
   pthread_t threads[NUM_THREADS];
   struct thread_data td[NUM_THREADS];
   int rc;
   int i;
 
   for( i=0; i < NUM_THREADS; i++ ){
      cout <<"main() : creating thread, " << i << endl;
      td[i].thread_id = i;
      td[i].message = (char*)"This is message";
      rc = pthread_create(&threads[i], NULL,
                          PrintHello, (void *)&td[i]);
      if (rc){
         cout << "Error:unable to create thread," << rc << endl;
         exit(-1);
      }
   }
   pthread_exit(NULL);
}
$ g++ -Wno-write-strings test.cpp -lpthread -o test.o
$ ./test.o
main() : creating thread, 0
main() : creating thread, 1
Thread ID : 0 Message : This is message
main() : creating thread, Thread ID : 21
 Message : This is message
main() : creating thread, 3
Thread ID : 2 Message : This is message
main() : creating thread, 4
Thread ID : 3 Message : This is message
Thread ID : 4 Message : This is message

连接和分离线程

#include <iostream>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>
 
using namespace std;
 
#define NUM_THREADS     5
 
void *wait(void *t)
{
   int i;
   long tid;
 
   tid = (long)t;
 
   sleep(1);
   cout << "Sleeping in thread " << endl;
   cout << "Thread with id : " << tid << "  ...exiting " << endl;
   pthread_exit(NULL);
}
 
int main ()
{
   int rc;
   int i;
   pthread_t threads[NUM_THREADS];
   pthread_attr_t attr;
   void *status;
 
   // 初始化并设置线程为可连接的(joinable)
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
 
   for( i=0; i < NUM_THREADS; i++ ){
      cout << "main() : creating thread, " << i << endl;
      rc = pthread_create(&threads[i], NULL, wait, (void *)&i );
      if (rc){
         cout << "Error:unable to create thread," << rc << endl;
         exit(-1);
      }
   }
 
   // 删除属性,并等待其他线程
   pthread_attr_destroy(&attr);
   for( i=0; i < NUM_THREADS; i++ ){
      rc = pthread_join(threads[i], &status);
      if (rc){
         cout << "Error:unable to join," << rc << endl;
         exit(-1);
      }
      cout << "Main: completed thread id :" << i ;
      cout << "  exiting with status :" << status << endl;
   }
 
   cout << "Main: program exiting." << endl;
   pthread_exit(NULL);
}
main() : creating thread, 0
main() : creating thread, 1
main() : creating thread, 2
main() : creating thread, 3
main() : creating thread, 4
Sleeping in thread 
Thread with id : 4  ...exiting 
Sleeping in thread 
Thread with id : 3  ...exiting 
Sleeping in thread 
Thread with id : 2  ...exiting 
Sleeping in thread 
Thread with id : 1  ...exiting 
Sleeping in thread 
Thread with id : 0  ...exiting 
Main: completed thread id :0  exiting with status :0
Main: completed thread id :1  exiting with status :0
Main: completed thread id :2  exiting with status :0
Main: completed thread id :3  exiting with status :0
Main: completed thread id :4  exiting with status :0
Main: program exiting.

C++11之后有了标准的线程库

#include <iostream>

#include <thread>

std::thread::id main_thread_id = std::this_thread::get_id();

void hello()  
{
    std::cout << "Hello Concurrent World\n";
    if (main_thread_id == std::this_thread::get_id())
        std::cout << "This is the main thread.\n";
    else
        std::cout << "This is not the main thread.\n";
}

void pause_thread(int n) {
    std::this_thread::sleep_for(std::chrono::seconds(n));
    std::cout << "pause of " << n << " seconds ended\n";
}

int main() {
    std::thread t(hello);
    std::cout << t.hardware_concurrency() << std::endl;//可以并发执行多少个(不准确)
    std::cout << "native_handle " << t.native_handle() << std::endl;//可以并发执行多少个(不准确)
    t.join();
    std::thread a(hello);
    a.detach();
    std::thread threads[5];                         // 默认构造线程

    std::cout << "Spawning 5 threads...\n";
    for (int i = 0; i < 5; ++i)
        threads[i] = std::thread(pause_thread, i + 1);   // move-assign threads
    std::cout << "Done spawning threads. Now waiting for them to join:\n";
    for (auto &thread : threads)
        thread.join();
    std::cout << "All threads joined!\n";
}

g++ -std=c++11 test.cpp -lpthread

C++Web编程


标签:std,main,int,编程,namespace,高级,C++,线程,include
From: https://blog.51cto.com/u_11797608/6400996

相关文章

  • c++11 tuple
    tuple看似简单,其实它是简约而不简单,可以说它是c++11中一个既简单又复杂的东东,关于它简单的一面是它很容易使用,复杂的一面是它内部隐藏了太多细节,要揭开它神秘的面纱时又比较困难。tuple是一个固定大小的不同类型值的集合,是泛化的std::pair。和c#中的tuple类似,但是比c#中的tuple......
  • 你用过最丑的编程语言是哪个?
    啥也不想多说,就是内个内个内个极简主义的Brainf0ck编程语言!!!丑不丑的话另说,关键是你不能变态吧!!!这个编程语言主打极简,所以就只有8种指令,这些指令是由一组符号组成的,反正人家就是真的很极简!但是极简到能让你原地爆炸,想起它我就血压飙升!下面先给大家瞅瞅这8种指令哈,它们所对应的字符命......
  • maven高级特性
    一、资源配置、资源加载属性值  之后打包,在打包的jar包中看属性:二、多环境配置,有些配置在测试环境 ......
  • asyncio:python3未来并发编程主流、充满野心的模块
    https://www.cnblogs.com/traditional/p/11828780.html楔子asyncio是Python在3.5版本中正式引入的标准库,这是Python未来并发编程的主流,非常重要的一个模块。有一个Web框架叫sanic,就是基于asyncio,使用sanic可以达到匹配Go语言的并发量(有点夸张了,还是有差距的,但......
  • proc c++连接oracle
    环境:oracle11g、vs20221、编写pc文件EXECSQLBEGINDECLARESECTION;structdatatable{intid;charname[30];}data;EXECSQLENDDECLARESECTION;ViewCode//#defineSQLCA_STORAGE_CLASSextern//增加该行报错EXECSQLINCLUDEconnect_oracle.h;......
  • [C++学习]关键字
    const关键字const关键字表示该数据类型的值只读,不可赋值,也就意味着它必须初始化。const若是修饰常量,则只读变量的值会放在“符号常量表”中,而不会立即开辟空间,当数据取地址时则会开辟空间。constintdata=100;int*p=(int*)&data;//开辟空间,p指向该空间。*p=200......
  • 面向切面编程和面向接口编程的区别
    面向切面编程:手段:分离业务的主逻辑和次逻辑的一种思想。目的:解决的是逻辑分离问题(主逻辑和次逻辑分开,其实主要是分离业务逻辑和非业务逻辑分开)。案例:我们开发项目的时候基本都要去连接数据库操作数据等,但是都会涉及到事务的提交,这时我们就用到了面向切面编程,我们在业务层只写自己......
  • 曲线艺术编程 coding curves 第二章 三角函数曲线(TRIG CURVES)
    第二章三角函数曲线(TRIGCURVES)原作:KeithPeters原文:https://www.bit-101.com/blog/2022/11/coding-curves/译者:池中物王二狗(sheldon)blog:http://cnblogs.com/willian/源码:github:https://github.com/willian12345/coding-curves曲线艺术编程系列第二章这是与曲线......
  • 编程语言中的编码知识
    序论在计算机内部,所有的信息最终都表示为一个二进制的字符串。每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte)。也就是说,一个字节一共可以用来表示256种不同的状态,每一个状态对应一个符号,就是256个符号,从00000000到11111111。ASCII......
  • C++字符转码(GBK和UTF8)
    std::stringGB2312ToUtf8(constchar*pSrc,intnLen){stringstrOut;if(pSrc&&nLen>0){//ANSI->UNICODEintlen=MultiByteToWideChar(CP_ACP,0,(LPCTSTR)pSrc,nLen,NULL,0);WCHAR*wszUtf8=new......