c++高级教程[自用]

参考自 菜鸟教程

文件和流

对文件的操作需要使用C++ 中另一个标准库 fstream,它定义了3种新的数据类型
在这里插入图片描述

打开文件

使用open函数打开文件,指定文件名和打开模式

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

ifstream的默认打开模式是ios::in,ofstream的默认打开模式是ios::out,fstream的默认打开模式是ios::in||ios::out
有以下几种打开模式
在这里插入图片描述
示例:使用ofstream打开文件,模式为写入+截断

#include <fstream>
using namespace std;
ofstream file;
file.open("file.txt",ios::out||ios::trunc);
//或者不指定打开模式 file.open("file.txt");

关闭文件

使用close函数关闭文件

void close();

读写文件

将cout替换为ofstream/ifstream/fstream的对象,使用<<写文件,使用<<读文件
读写实例:

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

文件位置指针

重新定位文件位置指针的成员函数:seekg(设置输入流的文件指针),seekp(设置输出流的文件指针)


// 定位到 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 );

异常

异常处理涉及3个关键字:

  • throw:抛出异常
  • catch:捕捉异常
  • try:try块放置可能抛出异常的代码
    实例
#include <iostream>
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;
}

如果想让catch捕捉所以异常,可以使用…

try
{
   // 保护代码
}catch(...)
{
  // 能处理任何异常的代码
}

exception类

std::exception是所有标准 C++ 异常的父类。
在这里插入图片描述
示例:

#include <iostream>
#include <exception>
using namespace std;
 
struct MyException : public exception
{
  //what ()函数,用于获取描述异常的 C 风格字符串
  //const 和throw ()是函数的修饰符。const:函数不会修改调用它的对象的状态,
  //throw():函数不抛出任何异常,等价于noexcept
  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.what() << std::endl;
  }
  catch(std::exception& e)
  {
    //其他的错误
  }
}

动态内存

C++ 程序中的内存分为两个部分:

  • 在函数内部声明的所有变量都将占用栈内存。new关键字创建的变量除外。
  • 这是程序中未使用的内存,在程序运行时可用于动态分配内存。

new和delete

  • new运算符为任意类型的变量分配处于堆的动态内存,并返回变量地址。
double* pvalue  = NULL; // 初始化为 null 的指针
pvalue  = new double;   // 为变量请求内存
  • 如果new运算符返回NULL,则表示是自由存储区已被用完,无法分配内存。
  • delete:使用 delete 操作符释放它所占用的内存
delete pvalue

数组的动态内存分配

一维数组:

int *array = new int[20];
delete [] array;

二维数组:

//动态分配int[n][m]的内存
int **array = new int *[n]
for(int i = 0;i<n;i++){
array[i] = new int[m];
}
//释放
for(int i = 0;i<n;i++){
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;
}

命名空间

命名空间作为附加信息来区分不同库中相同名称的函数、类、变量等。使用了命名空间即定义了上下文。
本质上,命名空间就是定义了一个范围。

命名空间的定义和调用

  • 定义:
namespace namespace_name {
   // 代码声明
}
  • 调用命名空间中的变量或函数:
    • namespacename::func()/namespacename::变量
    • 使用using namespace namespacename提前声明命名空间,那么后续代码中使用命名空间的元素可以不加前缀
    • 即使当前文件中定义了命名空间,但没有使用using namespace namespacename提前声明命名空间,那么后续代码中使用命名空间的元素必须加前缀
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 指令,这样在使用命名空间时就可以不用在前面加上命名空间的名称。
这个指令会告诉编译器,后续的代码将使用指定的命名空间中的名称。
可以使用多个命名空间。
示例:

#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;
}
  • using指令也可以使用命名空间的特定函数或变量
    示例:
using std::cout;

使用了上边的这行代码,代码中可以直接使用cout函数。但std库中的其他函数和变量仍然需要使用std::前缀

不连续的命名空间

下边这段代码可以是定义一个新的命名空间,也可以是为命名空间增加新的元素。

namespace namespace_name {
   // 代码声明
}

嵌套的命名空间

namespace namespace_name1 {
   // 代码声明
   namespace namespace_name2 {
      // 代码声明
   }
}

全局命名空间

在 C++ 中,如果你没有显式地指定命名空间,代码将位于默认命名空间中。
默认命名空间是一个没有名字的全局命名空间,通常被称为全局命名空间。
如果全局命名空间和其他命名空间的函数/变量名重复,编译器优先使用全局命名空间的函数/变量。

如果文件 A 中使用了 using namespace std;,而文件 B 包含了 A 的头文件但没有使用 using namespace std;,那么文件 B 本身不会自动引入 std 命名空间。

一个烧脑示例

#include <iostream>
using namespace std;
namespace A
{
    int a = 100;
    namespace B            //嵌套一个命名空间B
    {
        int a =20;
    }
}

int a = 200;//定义一个全局变量


int main(int argc, char *argv[])
{
    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,用于当有同名的局部变量时来区别两者。

模板

函数模板

template <typename 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;
}

类模板

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;  // 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++ 语句,所以它们不会以分号(;)结尾。

define预处理

#define 预处理指令用于创建符号常量。该符号常量通常称为宏。
宏是一种简单的文本替换机制。

#define macro-name replacement-text 

例如

#define PI 3.14159

当这define出现在一个文件中时,在该文件中后续出现的所有宏都将会在程序编译之前被替换为 replacement-text。

参数宏

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

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

条件宏通常用于一些简单的文本替换和条件编译的场景,而函数更适用于定义和执行复杂的代码逻辑。在现代 C++ 中,推荐使用函数而不是宏,因为函数更具有可读性、可维护性,并且避免了宏的一些潜在问题。

条件编译

如果在指令 #ifdef DEBUG 之前已经定义了符号常量 DEBUG,则会对程序中的 cerr 语句进行编译。

#ifdef DEBUG
   cerr <<"Variable x = " << x << endl;
#endif

可以使用 #if 0 语句注释掉程序的一部分

#if 0
   不进行编译的代码
#endif

示例:

#include <iostream>
using namespace 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 function" << endl;
#endif
 
#if 0
   /* 这是注释部分 */
   cout << MKSTR(HELLO C++) << endl;
#endif
 
   cout <<"The minimum 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

#和##运算符

#运算符会把 replacement-text 令牌转换为用引号引起来的字符串。

两个示例的结果都是”输出字符串“

#define func(c) #c
cout<< func(输出字符串);

##运算符用于连接两个令牌。

#define func(b,c) b##c
string bc = "输出字符串";
cout<< func(b,c);

预定义宏

在这里插入图片描述

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

信号

在这里插入图片描述

捕捉信号 signal函数

signal(registered signal, signal handler)

这个函数接收两个参数:第一个参数是要设置的信号的标识符,第二个参数是指向信号处理函数的指针
函数返回值是一个指向先前信号处理函数的指针。如果先前没有设置信号处理函数,则返回值为 SIG_DFL。如果先前设置的信号处理函数为 SIG_IGN,则返回值为 SIG_IGN。
示例:捕捉SIGINT信号

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

当按下ctrl+c键 :

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

生成信号

#include <iostream>
#include <csignal>
#include <unistd.h>
 
using namespace std;
int raise (signal sig);
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;
}

sleep 挂起

#include <windows.h>
sleep(100);//挂起100ms

linux:

#include <unistd.h> 
sleep(10);//挂起10s

STL

C++ STL(标准模板库)是一套功能强大的 C++ 模板类,提供了通用的模板类和函数,这些模板类和函数可以实现多种流行和常用的算法和数据结构,如向量、链表、队列、栈。
C++ 标准模板库的核心包括以下三个组件:
在这里插入图片描述

标准库

https://www.runoob.com/cplusplus/cpp-standard-library.html

  • 19
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值