C++学习9——菜鸟教程自学

C++异常处

异常是程序执行期间产生的问题。C++已成是指在程序运行发生的特殊情况,比如尝试除以零之类的操作。异常提供了一种转移程序控制权的方式。C++异常处理涉及到三个关键字:try、catch、throw。

throw:当出现问题时,程序会抛出一个异常;

catch:在想要处理问题的地方,通过异常处理程序捕获异常;

try:try代码块中的代码标识被激活的特定异常。

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

try
{
//保护代码
}catch (Exception Name e1)
{
//catch块
}catch (Exception Name e2)
{
//catch块
}catch (Exception Name e3)
{
//catch代码块
}

如果try块在不同的情境下会抛出不同的异常,这个时候可以尝试罗列多个catch语句,用于捕获不同类型的异常。

抛出异常

用户可以使用throw语句在代码块中的任何地方抛出异常。throw语句的操作数可以是任何表达式,表达式的结果的类型决定了抛出的异常类型。

以下是尝试除以零时抛出异常的实例:

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

捕获异常

catch块跟在try块后面,用于捕获异常。可以指定想要捕获的异常类型,职责是由catch关键字后的括号内的异常声明决定的。

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

上面的代码会捕获一个类型为ExceptionName的异常。如果想让catch块能处理try块抛出的任何类型的异常,则必须在异常声明的括号内使用省略号...。

C++标准的异常

C++提供了一系列标准的异常,定义在<exception>中,我们可以在程序中使用这些标准的异常。Std:exception        该异常是C++所有异常的父类

std::bad_alloc        该异常可以通过new抛出

std::bad_cast        该异常可以通过dynamic_cast抛出

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

std::typeid   该异常可以通过typeid抛出

std::logic_error        理论上可以通过读代码来检测到的异常

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

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

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

std::out_of _range        该异常可以通过方法抛出

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

std::range_error        当存储超过范围的值时,会抛出该异常

std::overflow_error        当发生数学上溢时,会抛出该异常

std::underflow_error        当发生数学下溢时,会抛出该异常

定义新的异常

可以通过继承和重载exception类来定义新的异常。实例如下:

#include<iostream>
#include<exception>
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::out<<e.what()<<std:endl;
}
catch(std::exception& e)
{
//其他的错误
}
}

这里,what()是异常类提供的一个公共方法,它被所有子异常重载,这将返回异常产生的原因。

C++动态内存

C++的程序分为两个部分:

栈:在函数内部声明的所有变量都将占用栈内存;

堆:这是程序中未使用的内存,在程序运行过程中用于动态分配内存

很多时候,用户无法预知需要多少内存来存储某个定义变量的特定信息,所需内存在运行时才能确定。在C++中,用户可以使用特殊的运算符为给定类型的变量在运行时分配堆内的内存,这回返回所分配的空间地址。这种运算符即w运算符。如果不需要动态分配内存空间,可以使用delete运算符,删除之前由new运算符分配的内存。

new和delete运算符

new data-type

在这里,new-type可以是包括数组在内的任意内置的数据类型,也可以是包括类或结构在内的用户自定义的任何数据类型给。

double* pvalue = NULL; //初始化为null的指针
pvalue = new double; //为变量请求内存

malloc()函数在C语言中就出现了,在C++中仍旧存在。但是不建议使用malloc(),new与malloc()函数相比,其主要的优点是,new不只是分配了内存,它还创建了对象。

在任何时候,当用户觉得某个某个已经动态分配的内存的变量不再需要使用时,可以使用delete操作符释放它所占用的内存。

delete pvalue;  //释放pvalue所指向的内存

数组的动态分配内存

对象的动态内存分配

C++命名空间

因为会出翔命名重复的情况,因此,C++中引入命名空间的概念,用来解决这个问题。它可以作为附加信息来区分不同库中相同名称的函数、类、变狼等。使用了命名空间即定义了上下文。本质上,命名空间定义了一个范围。

定义命名空间

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_space::func();

//调用第二个命名空间中的函数
second_space::func();

return 0;
}

using指令

 可以使用using namespace指令,这样在使用命名空间时就可以不用在前面加上命名空间的名称。这个指令会告诉编译器,后续的代码将使用指定的命名空间中的名称。

using指令也可以用来指定特定命名空间中的特定项目,比如若只是打算使用std命名空间中的cout,则可以使用如下语句:

using std::cout;

using指令引入的名称遵循正常的范围规则,名称从使用using指令开始是可见的,直到该范围结束。此时,在范围外定义的同名实体是隐藏的。

不连续的命名空间

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

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

嵌套的命名空间

命名空间可以嵌套,可以在一个命名空间内定义另一个命名空间。可以通过::运算符来访问嵌套的命名空间。

C++模板

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

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

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

函数模板

一般形式如下:

template<typename <type> ret-type func-name(parameter list)

{

//函数的主体

}

type是函数所使用的数据类型的占位符名称。这个名称可以在函数定义中使用。

类模板

template <class type> class class-name{

.

.

.

}

type是占位符的名称,可以在类实例化的时候进行指定。可以使用一个逗号分隔的列表来定义多个泛型数据类型。

C++预处理器

预处理器是一些指令,指示编译器在实际编译之前所需完成的预处理。所有预处理指令都是以#开头,只有空格字符可以出现在预处理指令之前。预处理指令不是C++语句,所以它们不会以分号结尾。

#define预处理

#define预处理用于创建符号常量。该符号常量通常称为宏,指令的一般形式是:

#define macro-name replacement-text

参数宏

可以使用#define来定义一个有参数的宏

条件编译

有几个指令可以用来有选择地对部分程序源代码进行编译。这个过程称为条件编译。

条件预处理器的结构与if选择结构很像。

#和##运算符

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

C++中的预定义宏

C++提供了以下预定义宏:

_LINE_        这会在程序编译时包括当前行号

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

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

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

C++信号处理

信号是由操作系统传给进程的中断,会提早终止一个程序。有些信号不能被程序捕获,但是下面所列信号可以在程序中捕获,并额可以基于信号采取适当的动作。这些信号定义在C++头文件<csignal>中。

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

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

SIGINT        程序终止(interrupt)信号

SIGSEGV        非法访问内存

SICTERM        发送到车程序的终止请求

signal()函数

C++信号处理库提供了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(SICINT,signalHandler);

while(1){
cout<<"Going to sleep..." << endl;
sleep(1);
}
return 0;
}

 raise()函数

可以使用raise()函数生成信号,该函数有一个整数信号编号作为参数,语法如下:

int raise(signal,sig);

这里,sig是要发送的信号的编号,这些信号包括SIGINT、SIGBART、SIGFPE、SIGILL、SIGTERM、SIGHUP。如下为使用raise()函数生成信号的实例。

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

using namespace std;

void signalHandler(int fignum)
{
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++多线程

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

基于进程的多任务处理是程序的并发执行;

基于线程的多任务处理是统同一程序的片段的并发执行。

多线程程序包括可以同时运行的两个或多个部分。这样的程序中的每个部分称为一个线程,每个线程定义了一个单独的执行路径。

创建线程

下面的程序,可以用来创建一个POSIX线程:

#include <pathread.h>

pthread_create(thread, attr, start_contine,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>

using namespace std;

#define NUM_THREADS 5

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

向线程传递参数

可以通过结构传递多个参数,可以在线程回调中传递任意结构的数据类型,因为它指向void。

#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);
}

 连接和分离线程

可以使用一下两个函数来连接或分离线程:

pthread_join (threadid, status)

pthread_detach (threadid)

pthread_join()子程序阻碍调用程序,直到指定的threadid线程终止为止。当创建一个线程时,它的某个属性会定义它是否是可连接的(joinalbe)或可分离的(detached)。只有创建时定义为可连接的线程才可以被连接。如果线程创建时被定义为可分离的,则它永远也不能被连接。

#include<iostream>
#include<cstdlib>
#include<pthread.h>
#<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_THREAD;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);
}
}
//删除属性,并等待其他进程
thread_attr_destory(&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);
}

C ++Web编程

什么是CGI?

公共网关接口(CGI),是一套标准,定义了信息是如何在web服务器和客户端脚本之间交换的。

CGI规范目前是由NCSA维护的,NCSA定义CGI如下:

        公共网关接口(CGI),是一种用于外部网关程序于信息服务器(如HTTP服务器)对接的接口标准。

web浏览

公共网关接口是使得应用程序能够与web服务器以及客户端进行交互的标准协议。这些程序可用多种语言编写。

web服务器配置

所有由HTTP配置的CGI程序,都必须在预配置的目录中,即CGI目录。

接下来是关于更进一步的网络相关的知识,因此在别的模块中学习。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王辞夜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值