C++编程

目录

常见函数:

atoi()函数(字符串转数字)

sprintf()(数字转字符串)

c_str()函数

基本功能实现

并发编程

条件变量(condition_variable)

基本函数:

.emplace_back、.pop_front

取绝对值

线程和死锁

2.1 多进程并发

2.2 多线程并发

异常处理

一、try中带有return

二、catch中带有return

三、finally中带有return

总结

计算程序的运行时间


常见函数:

atoi()函数(字符串转数字)

atoi()原型:  int atoi(const char *str );

函数功能:把字符串转换成整型数。

参数str:要进行转换的字符串

返回值:每个函数返回 int 值,此值由将输入字符作为数字解析而生成。 如果该输入无法转换为该类型的值,则atoi的返回值为 0。

注意:使用该函数时要注意atoi返回的是int类型,注意输入str的范围不要超出int类型的范围。

sprintf()(数字转字符串)

char text[128];
sprintf(text,"这是第%d个文件",i);

c_str()函数()

把C++中的string的字符串转换成c中的字符串格式。

语法: 
const char *c_str();
c_str()函数返回一个指向正规C字符串的指针常量, 内容与本string串相同. 
这是为了与c语言兼容,在c语言中没有string类型,故必须通过string类对象的成员函数c_str()把string 对象转换成c中的字符串样式。
注意:一定要使用strcpy()函数 等来操作方法c_str()返回的指针 
比如:最好不要这样: 
char* c; 
string s="1234"; 
c = s.c_str(); //c最后指向的内容是垃圾,因为s对象被析构,其内容被处理,同时,编译器也将报错——将一个const char *赋与一个char *。

应该这样用: 
char c[20]; 
string s="1234"; 
strcpy(c,s.c_str()); 
这样才不会出错,c_str()返回的是一个临时指针,不能对其进行操作
   c_str()返回的是一个分配给const char*的地址,其内容已设定为不可变更,如果再把此地址赋给一个可以变更内容的char*变量,就会产生冲突,在2010中是不被允许的。但是如果放入函数调用,或者直接输出,因为这些函数和输出都是把字符串指针作为 const char*引用的,所以不会有问题。

再举个例子
c_str() 以 char* 形式传回 string 内含字符串
如果一个函数要求char*参数,可以使用c_str()方法: 
string s = "Hello World!";
printf("%s", s.c_str()); //输出 "Hello World!"


.emplace_back、.pop_front

.emplace_back  其功能和 push_back() 相同,都是在 vector 容器的尾部添加一个元素。

删除第一个或最后一个元素—–pop_front 和pop_back 函数

删除容器内所有的元素—clear函数

front函数:返回当前vector容器中起始元素的引用

基本功能实现

并发编程

条件变量(condition_variable)

取绝对值

不同类型的数据使用不同类型的绝对值函数:
整型:
int abs(int i)  //返回整型参数i的绝对值 

复数:
double cabs(struct complex znum)  //返回复数znum的绝对值  

双精度浮点型:
double fabs(double x)  //返回双精度参数x的绝对值    

长整型:
long labs(long n)  //返回长整型参数n的绝对值 

线程和死锁

多线程与多进程是并发的两种途径。想象两个场景:

  • 场景一:你和小伙伴要开发一个项目,但小伙伴们放寒假都回家了,你们只能通过QQ聊天、手机通话、发送思维导图等方式来进行交流,总之你们无法很方便地进行沟通。好处是你们各自工作时可以互不打扰。
  • 场景二:你和小伙伴放假都呆在学校实验室中开发项目,你们可以聚在一起使用头脑风暴,可以使用白板进行观点的阐述,总之你们沟通变得更方便有效了。有点遗憾的是你在思考时可能有小伙伴过来问你问题,你受到了打扰。

这两个场景描绘了并发的两种基本途径。每个小伙伴代表一个线程,工作地点代表一个处理器。场景一中每个小伙伴是一个单线程的进程,他们拥有独立的处理器,多个进程同时执行;场景二中只有一个处理器,所有小伙伴都是属于同一进程的线程。

2.1 多进程并发

多个进程独立地运行,它们之间通过进程间常规的通信渠道传递讯息(信号,套接字,文件,管道等),这种进程间通信不是设置复杂就是速度慢,这是因为为了避免一个进程去修改另一个进程,操作系统在进程间提供了一定的保护措施,当然,这也使得编写安全的并发代码更容易。运行多个进程也需要固定的开销:进程的启动时间,进程管理的资源消耗。

2.2 多线程并发

在当个进程中运行多个线程也可以并发。线程就像轻量级的进程,每个线程相互独立运行,但它们共享地址空间,所有线程访问到的大部分数据如指针、对象引用或其他数据可以在线程之间进行传递,它们都可以访问全局变量。进程之间通常共享内存,但这种共享通常难以建立且难以管理,缺少线程间数据的保护。因此,在多线程编程中,我们必须确保每个线程锁访问到的数据是一致的。C++11中引入了一个用于多线程操作的thread类,简单多线程示例:

#include <iostream> 
#include <thread> 
#include <Windows.h> 
using namespace std; 
void thread01() 
{ 
    for (int i = 0; i < 5; i++) 
    { 
        cout << "Thread 01 is working !" << endl; 
        Sleep(100); 
    } 
} 
void thread02() 
{ 
    for (int i = 0; i < 5; i++) 
    { 
        cout << "Thread 02 is working !" << endl; 
        Sleep(200); 
    } 
} 
int main() 
{ 
    thread task01(thread01); 
    thread task02(thread02); 
    task01.join(); 
    task02.join(); 
    for (int i = 0; i < 5; i++) 
    { 
        cout << "Main thread is working !" << endl; 
        Sleep(200); } system("pause"); 
    }
}

输出:

两个子线程并行执行,join函数会阻塞主流程,所以子线程都执行完成之后才继续执行主线程。可以使用detach将子线程从主流程中分离,独立运行,不会阻塞主线程:

#include <iostream>
#include <thread>
#include <Windows.h>

using namespace std;

void thread01()
{
	for (int i = 0; i < 5; i++)
	{
		cout << "Thread 01 is working !" << endl;
		Sleep(100);
	}
}
void thread02()
{
	for (int i = 0; i < 5; i++)
	{
		cout << "Thread 02 is working !" << endl;
		Sleep(200);
	}
}

int main()
{
	thread task01(thread01);
	thread task02(thread02);
	task01.detach();
	task02.detach();

	for (int i = 0; i < 5; i++)
	{
		cout << "Main thread is working !" << endl;
		Sleep(200);
	}
	system("pause");
}

输出:

使用detach的主线程和两个子线程并行执行。

带参子线程

在绑定的时候也可以同时给带参数的线程传入参数:

#include <iostream>
#include <thread>
#include <Windows.h>

using namespace std;

//定义带参数子线程
void thread01(int num)
{
	for (int i = 0; i < num; i++)
	{
		cout << "Thread 01 is working !" << endl;
		Sleep(100);
	}
}
void thread02(int num)
{
	for (int i = 0; i < num; i++)
	{
		cout << "Thread 02 is working !" << endl;
		Sleep(200);
	}
}

int main()
{
	thread task01(thread01, 5);  //带参数子线程
	thread task02(thread02, 5);
	task01.detach();
	task02.detach();

	for (int i = 0; i < 5; i++)
	{
		cout << "Main thread is working !" << endl;
		Sleep(200);
	}
	system("pause");
}

输出跟上例输出一样:

多线程数据竞争

多个线程同时对同一变量进行操作的时候,如果不对变量做一些保护处理,有可能导致处理结果异常:

#include <iostream>
#include <thread>
#include <Windows.h>

using namespace std;

int totalNum = 100;

void thread01()
{
	while (totalNum > 0)
	{
		cout << totalNum << endl;
		totalNum--;
		Sleep(100);
	}
}
void thread02()
{
	while (totalNum > 0)
	{
		cout << totalNum << endl;
		totalNum--;
		Sleep(100);
	}
}

int main()
{
	thread task01(thread01);
	thread task02(thread02);
	task01.detach();
	task02.detach();
	system("pause");
}

输出结果(部分):

有两个问题,一是有很多变量被重复输出了,而有的变量没有被输出;二是正常情况下每个线程输出的数据后应该紧跟一个换行符,但这里大部分却是另一个线程的输出。

这是由于第一个线程对变量操作的过程中,第二个线程也对同一个变量进行各操作,导致第一个线程处理完后的输出有可能是线程二操作的结果。针对这种数据竞争的情况,可以使用线程互斥对象mutex保持数据同步。

mutex类的使用需要包含头文件mutex:

#include <iostream>
#include <thread>
#include <Windows.h>
#include <mutex>

using namespace std;

mutex mu;  //线程互斥对象

int totalNum = 100;

void thread01()
{
	while (totalNum > 0)
	{
		mu.lock(); //同步数据锁
		cout << totalNum << endl;
		totalNum--;
		Sleep(100);
		mu.unlock();  //解除锁定
	}
}
void thread02()
{
	while (totalNum > 0)
	{
		mu.lock();
		cout << totalNum << endl;
		totalNum--;
		Sleep(100);
		mu.unlock();
	}
}

int main()
{
	thread task01(thread01);
	thread task02(thread02);
	task01.detach();
	task02.detach();
	system("pause");
}

多线程中加入mutex互斥对象之后输出正常:

但是使用mutex是不安全的,当一个线程在解锁之前异常退出了,那么其它被阻塞的线程就无法继续下去。

std::lock_guard

  使用lock_guard则相对安全,它是基于作用域的,能够自解锁,当该对象创建时,它会像m.lock()一样获得互斥锁,当生命周期结束时,它会自动析构(unlock),不会因为某个线程异常退出而影响其他线程。示例:

int cnt = 20;
mutex m;
void t1()
{
    while (cnt > 0)
    {    
        lock_guard<mutex> lockGuard(m);
        if (cnt > 0)
        {
            --cnt;
            cout << cnt << endl;
        }
        
    }
}
void t2()
{
    while (cnt > 0)
    {
        lock_guard<mutex> lockGuard(m);
        if (cnt > 0)
        {
            --cnt;
            cout << cnt << endl;
        }
    
    }
}

异常处理

异常处理中,大家都知道try、catch、finally是按顺序执行的。即:

  • 如果try中没有异常,则顺序为try→finally

  • 如果try中有异常,则顺序为try→catch→finally

  但是当try、catch、finally中加入return之后,就会有几种不同的情况出现。

一、try中带有return

private int testReturn1() {
        int i = 1;
        try {
            i++;
            System.out.println("try:" + i);
            return i;
        } catch (Exception e) {
            i++;
            System.out.println("catch:" + i);
        } finally {
            i++;
            System.out.println("finally:" + i);
        }
        return i;
    }

  输出:

try:2
finally:3
2

  因为当try中带有return时,会先执行return前的代码,然后暂时保存需要return的信息,再执行finally中的代码,最后再通过return返回之前保存的信息。所以,这里方法返回的值是try中计算后的2,而非finally中计算后的3。但有一点需要注意,再看另外一个例子:

private List<Integer> testReturn2() {
        List<Integer> list = new ArrayList<>();
        try {
            list.add(1);
            System.out.println("try:" + list);
            return list;
        } catch (Exception e) {
            list.add(2);
            System.out.println("catch:" + list);
        } finally {
            list.add(3);
            System.out.println("finally:" + list);
        }
        return list;
    }

  输出:

try:[1]
finally:[1, 3]
[1, 3]

  看完这个例子,可能会发现问题,刚提到return时会临时保存需要返回的信息,不受finally中的影响,为什么这里会有变化?其实问题出在参数类型上,上一个例子用的是基本类型,这里用的引用类型。list里存的不是变量本身,而是变量的地址,所以当finally通过地址改变了变量,还是会影响方法返回值的。

二、catch中带有return

private int testReturn3() {
        int i = 1;
        try {
            i++;
            System.out.println("try:" + i);
            int x = i / 0 ;
        } catch (Exception e) {
            i++;
            System.out.println("catch:" + i);
            return i;
        } finally {
            i++;
            System.out.println("finally:" + i);
        }
        return i;
    }

  输出:

try:2
catch:3
finally:4
3

  catchreturntry中一样,会先执行return前的代码,然后暂时保存需要return的信息,再执行finally中的代码,最后再通过return返回之前保存的信息。所以,这里方法返回的值是try、catch中累积计算后的3,而非finally中计算后的4

三、finally中带有return

private int testReturn4() {
        int i = 1;
        try {
            i++;
            System.out.println("try:" + i);
            return i;
        } catch (Exception e) {
            i++;
            System.out.println("catch:" + i);
            return i;
        } finally {
            i++;
            System.out.println("finally:" + i);
            return i;
        }
    }

  输出:

try:2
finally:3
3

  当finally中有return的时候,try中的return会失效,在执行完finallyreturn之后,就不会再执行try中的return。这种写法,编译是可以编译通过的,但是编译器会给予警告,所以不推荐在finally中写return,这会破坏程序的完整性,而且一旦finally里出现异常,会导致catch中的异常被覆盖

总结

  1. finally中的代码总会被执行。

  2. try、catch中有return时,也会执行finallyreturn的时候,要注意返回值的类型(基本数值类型还是引用类型),是否受到finally中代码的影响。

  3. finally中有return时,会直接在finally中退出,导致try、catch中的return失效。

  4. trycatch中的return不可能同时执行

  5. trycatch中的return之后,函数最后的return不会执行

计算程序的运行时间

来源:https://www.cnblogs.com/zhiqiangliu/p/4904365.html

#include<iostream.h>
#include<time.h>
void main()
{
   clock_t start,finish;
   double totaltime;
   start=clock();

   ……                     //把你的程序代码插入到这里面

   finish=clock();
   totaltime=(double)(finish-start)/CLOCKS_PER_SEC;
   cout<<"\n此程序的运行时间为"<<totaltime<<"秒!"<<endl;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值