『 C++ 』异常

news2024/9/22 7:42:24

文章目录

    • 异常概念及使用
    • 自定义类型的异常
    • C++ 标准库的异常体系
    • 异常的重新抛出
    • 异常安全
    • 异常规范
    • 异常的优缺点


异常概念及使用

请添加图片描述

C语言常见的错误处理机制如下:

  • 返回值约定

    通过定义一些列的返回值以及其对应的错误信息表述,通过不同的返回值来查看当前函数是否与调用成功;

    通常情况下开发者需要检查返回值来判断是否发生错误;

    int main() {
      FILE* fp = fopen("log.txt", "r");
      if (fp == NULL) { // 返回值约定 如果返回空指针则表示打开文件错误
        cout << "fopen fail" << endl;
      }
      return 0;
    }
    /*
    	输出结果为:
    	$ ./test 
    	fopen fail
    */
    
  • errno变量

    C语言提供了一个全局变量errno,用于存储最近一次函数调用发生的错误代码;

    开发者可检查errno的值来获取更多的错误信息;

    int main() {
      FILE* fp = fopen("log.txt", "r");
      if (fp == NULL) {
        cout << strerror(errno) << endl;  // 使用strerror打印errno对应的错误信息
      }
      return 0;
    }
    /*
    	输出结果为:
    	$ ./test 
    	No such file or directory
    */
    
  • perror()函数

    这个函数可根据errno的值打印出一条错误信息以定位对应的错误;

    int main() {
      FILE* fp = fopen("log.txt", "r");
      if (fp == NULL) {
        perror("fopen fail\n"); // 使用perror打印最近的错误信息
      }
      return 0;
    }
    
    /*
    	输出结果为:
    	$ ./test 
    	fopen fail
    	: No such file or directory
    
    */
    
  • assert()

    assert()将直接终止程序,如内存错误,除零错误时;

异常是C++引入的一个错误处理机制,使用try-catch块来捕获和处理异常;

当函数抛出异常时控制流会转移到相应的catch块中处理;

通常使用throw抛异常,并使用try-catch来处理异常;

  • throw

    问题出现时,程序将使用throw抛一个异常;

    可抛出任意类型的异常,无论是内置类型还是自定义类型;

  • catch

    该关键字用于捕获异常,可以有多个catch进行捕获;

  • try

    try块中的代码标识将被激活特定异常,try块后可跟一个或是多个catch;

double Div(double num1, double num2) {
  double ret = 0;
  if (num2 == 0) {
    throw "Division by zero error";  // throw 抛异常
  } else {
    ret = num1 / num2;
  }
  cout << "the process executed sucess" << endl; // 捕获异常时不执行
  return ret;
}

int main() {
  try {  // 捕获异常
    cout << Div(10, 0) << endl;
    cout << "No exception was caught" << endl; // 捕获异常时不执行
  } catch (const char* erostr) {  // 处理异常
    cout << erostr << endl;
  }
}

throw可抛任意类型的异常,其中当异常被抛出后,出现异常的后续代码将不会执行,程序将逐层释放栈帧并直接跳转至catch异常处理部分;

这个例子运行结果为:

$ ./test 
Division by zero error

程序的运行是以一个执行链式的存在,即在内存中不停的压栈与出栈;

通常情况下捕获异常时程序将跳转到最近的最匹配的catch处对异常进行处理,此处的匹配指的是当一个异常被抛出时必须被一个catch以相同类型的方式进行接收,类似于函数的传参;

当异常处理结束后程序将继续向后执行(已释放栈帧的函数内容后续不再执行);

double Div(double num1, double num2) {
  double ret = 0;
  if (num2 == 0) {
    throw "Division by zero error";  // throw 抛异常
  } else {
    ret = num1 / num2;
  }
  cout << "the process executed sucess" << endl;  // 捕获异常时不执行
  return ret;
}

void func() {
  try  {  // func 捕获异常
    cout << Div(10, 0) << endl;
    cout << "No exception was caught" << endl;  // 捕获异常时不执行
  } catch (const char* erostr) {                // func 处理异常
    cout << "func : " << erostr << endl;
  }
}

int main() {
  try {  // main 捕获异常
    func();
  } catch (const char* erostr) {  // main处理异常
    cout << "main : " << erostr << endl;
  }
}

在这个例子中func()函数与main()函数依次尝试捕获异常,当异常被抛出后执行流将跳转至最近且最匹配的栈帧中的catch进行异常处理;

这个例子的执行结果为:

$ ./test 
func : Division by zero error # 异常处理在func中进行

# 同时抛出的异常已经在 func 中被处理
# main 函数中的 catch 在这次中不会再捕获到异常

当某个函数抛出一个异常时必须被捕获,若是一个异常被抛出没有被捕获,或者是没有以约定形式捕获(抛出了一个int类型的异常,但唯一的catchdouble类型),程序将直接终止;

double Div(double num1, double num2) {
  double ret = 0;
  if (num2 == 0) {
    throw "Division by zero error";  // throw 抛异常
  } else {
    ret = num1 / num2;
  }
  cout << "the process executed sucess" << endl;  // 捕获异常时不执行
  return ret;
}


int main() {
  try { 
    cout << Div(10, 0) << endl;
    cout << "No exception was caught" << endl;  
  } catch (int erostr) {   // 未按约定捕获异常            
    cout << "main : " << erostr << endl;
  }
}

/*
	运行结果为:
	 ./test 
	terminate called after throwing an instance of 'char const*'
	Aborted
	程序异常终止;
*/

自定义类型的异常

请添加图片描述

异常的抛出通常不使用单纯的内置类型,而是抛出一个自定义类型的异常;

通常这个自定义类型的异常为一个基类异常的派生类对象;

当抛异常抛出一个基类的派生类时无论是怎样的派生类,最终都可以被该基类的指针或是引用进行接收;

同时可在基类中以virtual关键字修饰一个虚函数,使派生类对该虚函数进行重写;

当基类对象的指针或是引用接收到一个派生类对象时可直接以多态的形式去调用其对应类型的虚函数从而使开发者明白具体在项目中的哪个模块抛出了异常出现了错误;

#include <iostream>
#include <string>

using namespace std;

// 基类:abnormalBase
class abnormalBase {
public:
    // 构造函数,初始化错误ID和错误信息
    abnormalBase(int id, const string& err) : erid_(id), errstr_(err) {}

    // 虚函数,用于输出错误信息
    virtual void what() {
        printf("the error id : %d\nwhat: %s\n", erid_, errstr_.c_str());
    }

    // 虚析构函数,确保派生类的析构函数被调用
    virtual ~abnormalBase() {}

protected:
    int erid_;      // 错误ID
    string errstr_; // 错误信息
};

// 派生类:abnormalDeriver
class abnormalDeriver : public abnormalBase {
public:
    // 构造函数,调用基类构造函数
    abnormalDeriver(int id, const string& er) : abnormalBase(id, er) {}

    // 重写基类的what()函数,输出派生类特定的错误信息
    void what() override {
        printf("Derived class error id: %d\nwhat: %s\n", erid_, errstr_.c_str());
    }
};

// 除法函数
double Div(double num1, double num2) {
    double ret = 0;
    // 检查除数是否为零
    if (num2 == 0) {
        // 创建异常对象并抛出
        abnormalDeriver* erptr = new abnormalDeriver(1, "Division by zero error");
        throw erptr; // 抛出异常指针
    } else {
        // 进行除法运算
        ret = num1 / num2;
    }
    cout << "the process executed successfully" << endl; // 捕获异常时不执行
    return ret; // 返回结果
}

// 函数func,调用Div函数
void func() { 
    Div(10, 0); // 这里会抛出异常
}

// 主函数
int main() {
    try {
        func(); // 调用func,可能会抛出异常
    } catch (abnormalBase* er) { // 捕获异常
        er->what(); // 调用异常对象的what()方法输出错误信息
        delete er; // 释放异常对象的内存
    }

    return 0; // 程序结束
}

在这个例子中定义了一个名为abnormalBase的基类,并写了一个名为what的虚函数;

其中将析构函数设定为虚函数是确保其派生类在析构时能够在析构派生类部分后再析构基类部分从而达到完全清理的功能;

该基类派生了一个abnormalDeriver的类,该类重写了基类的虚函数,使得能够在调用该函数时达到多态效果;

同样以除零错误为例,当出现除零错误时将用new实例化出一个异常对象,并抛出异常对象的指针;

对应的执行流将跳转到最近且最匹配的catch部分并对异常进行处理,在main函数中以基类指针接收派生类对象指针,同时其基类虚函数被派生类重写,构成多态条件,该处可直接调用what函数查看异常的错误信息;

不同的项目对于异常的处理都可能不同,可能在有些项目中需要提示异常的错误信息,而有些项目中需要遇到异常时对异常操作重新进行若干次的重试(可能是该操作的资源未就绪所导致的异常)等等;

常见的异常处理策略为如下:

  • 错误提示

    在某些项目中异常信息需要及时反馈给用户使其能够知道具体错误发生的部分从而定位错误信息;

    通常通过日志记录或用户界面提示实现;

  • 重试机制

    对于一些操作(网络请求或文件访问),可能因为资源未就绪而导致异常;

    此时可设计重试机制,则在捕获到异常后自动尝试该操作若干次,若是仍不成功则再进行提示用户信息等操作;

  • 资源清理

    确保在捕获异常时,正确释放占用的资源以避免内存泄漏或资源锁定;

  • 不同策略结合

    在复杂项目中可结合多种策略,如先进性重试机制,当重试至若干次数时则进行资源清理并错误提示等;

  • 自定义异常类型

    可通过项目需求定义多个自定义异常类型以便细致地处理不同种类的错误;


C++ 标准库的异常体系

请添加图片描述

C++标准库提供了一套异常处理机制,允许程序在运行时捕获和处理错误;

C++标准库定义了一个基本的异常类std::exception,所有的标准异常类都继承自这个类,其中该类提供了一个what()虚函数,用于返回异常的描述信息;

同时C++标准库提供了一系列的标准异常类,常见的包括:

  • std::runtime_error

    表示运行时错误;

  • std::logic_error

    表示逻辑错误,如违反了程序的逻辑;

  • std::out_of_range

    表示访问超出范围的错误;

  • std::invalid_argument

    表示传递给函数的参数无效;

  • std::bad_alloc

    表示内存分配失败;

这些异常类可根据不同的错误类型进行捕获和处理;

#include <iostream>
#include <stdexcept> // 包含标准异常类

using namespace std;

// 除法函数
double Div(double num1, double num2) {
    if (num2 == 0) {
        throw runtime_error("Division by zero error");  // 抛出标准异常
    }
    cout << "The process executed successfully" << endl;  // 捕获异常时不执行
    return num1 / num2;
}

int main() {
    try {
        cout << Div(10, 0) << endl; // 尝试除以零
        cout << "No exception was caught" << endl;
    } catch (const runtime_error& e) {
        cout << "main: " << e.what() << endl; // 捕获并处理异常
    }

    return 0; // 程序结束
}

在这个例子中调用Div函数,如果num2为零则抛出一个std::runtime_error异常;

这个例子的运行结果为:

$ ./test 
main: Division by zero error

C++标准库的异常体系通常较为复杂与混乱,不建议使用;


异常的重新抛出

请添加图片描述

异常的重新抛出指的是当一个函数抛出异常时首先让执行流跳跃至一个最近的且最匹配的catch处,在该catch中处理一些特定的操作后再将该异常重新抛出至较浅层的catch处对异常进行处理;

double Div(double num1, double num2) {
  if (num2 == 0) {
    throw runtime_error("Division by zero error");
  }
  cout << "The process executed successfully" << endl;
  return num1 / num2;
}

void func() {
  try {
    cout << Div(10, 0) << endl;
    cout << "No exception was caught" << endl;
  } catch (const runtime_error& e) {  // 第一次捕获异常
    cout << "func get a abnormal " << endl;
    throw e;  // 将异常重新抛出
  }
}

int main() {
  try {
    func();
  } catch (const runtime_error& e) {  // 最终捕获异常并对异常进行处理
    cout << "main handler a abonormal: " << e.what() << endl;
  }

  return 0;
}

在这个例子中调用了Div函数并在func函数中捕获了一场进行了一些特定处理,而后将异常重新抛出;

允许更高层次的代码(main函数)处理该异常;

这种机制允许在不同的层次上对异常进行处理,使得程序能够保持灵活性和可维护性;


异常安全

请添加图片描述

函数的调用可以类比于一个调用链,不停的在内存当中压栈以及释放;

当一个函数抛出一个异常时其执行流将直接跳转至最近且最匹配的catch处,同时逐层释放对应的栈帧;

此时该函数的栈结构以及对应的栈中的变量资源将被释放,但若是在该函数中在堆中开辟了空间时可能会导致执行流的跳转而未对该堆空间进行清理则会导致内存泄漏的问题,这种情况是异常安全的较为常见的异常安全问题;

class ObjectTest {
 public:
  ObjectTest() { cout << " ObjectTest()" << endl; }
  ~ObjectTest() { cout << " ~ObjectTest()" << endl; }
};

double Div(double num1, double num2) {
  if (num2 == 0) {
    throw runtime_error("Division by zero error");
  }
  cout << "The process executed successfully" << endl;
  return num1 / num2;
}

void func() {
  ObjectTest *t1 = new ObjectTest;  // 在堆上实例化一个对象
  ObjectTest *t2 = new ObjectTest;  // 在堆上实例化另一个对象

  cout << Div(10, 0) << endl; // 这里会抛出异常
  // 当抛出一个异常时,执行流将直接跳跃至最近且最匹配的 catch 处
  // Div 函数若是抛出了异常则会直接跳跃至 main 函数中的 catch 处
  // 下面的 delete 清理语句则不会执行

  delete t1; // 不会执行
  delete t2; // 不会执行
}

int main() {
  try {
    func(); // 调用 func,可能抛出异常
  } catch (const runtime_error &e) {
    cout << "main handler a abnormal: " << e.what() << endl;
  }

  return 0;
}

在这个例子中在堆中实例化了两个对象,在调用可能抛出异常的函数后对资源进行清理;

Div函数抛出异常后执行流将直接跳跃至main函数中的catch处,对应的func函数中的delete资源清理将不会被执行;

这个例子的运行结果为:

$ ./test 
 ObjectTest()
 ObjectTest()
 
# 只调用了构造函数 未调用析构函数 表示资源未正确清理

在这种情况下可使用异常的重新抛出对资源进行清理;

class ObjectTest {
 public:
  ObjectTest() { cout << " ObjectTest()" << endl; }
  ~ObjectTest() { cout << " ~ObjectTest()" << endl; }
};

double Div(double num1, double num2) {
  if (num2 == 0) {
    throw runtime_error("Division by zero error");
  }
  cout << "The process executed successfully" << endl;
  return num1 / num2;
}

void func() {
  ObjectTest *t1 = new ObjectTest;
  ObjectTest *t2 = new ObjectTest;

  try {
    cout << Div(10, 0) << endl;
  } catch (const runtime_error &e) {
    delete t1;
    delete t2;
    cout << "the t1 and d2 deleted sucessful,and throw abnormal again" << endl;
    throw e;  // 对堆空间上的资源进行清理而后将异常重新抛出
  }
}

int main() {
  try {
    func();
  } catch (const runtime_error &e) {
    cout << "main handler a abonormal: " << e.what() << endl;
  }
  return 0;
}

在这个例子中func函数在堆上实例化两个对象后再调用可能出现异常的函数时对对应异常进行捕获;

当异常被抛出时将先被func函数的catch捕获,此处并不作异常的处理,而是对无法自行清理的动态分配资源进行清理,而后将异常重新抛出;

最终异常会被main函数中的catch捕获并对异常进行处理;

这个例子的运行结果为:

$ ./test 
 ObjectTest()
 ObjectTest()
 ~ObjectTest()
 ~ObjectTest()
the t1 and d2 deleted sucessful,and throw abnormal again
main handler a abonormal: Division by zero error

# 析构函数被调用表示资源被正确清理

除了在newdelete之间抛出的异常导致内存泄漏所引发的异常安全以外,异常安全还有:

  • 在构造函数中抛异常

    通常不建议在构造函数中抛异常,构造函数主要完成对象的构造和初始化,若是在构造函数中抛异常可能导致对象不完整或是没有完全初始化;

  • 在析构函数中抛异常

    通常不建议在析构函数中抛异常,析构函数主要完成对象的资源清理工作,若是在析构函数中抛异常可能导致对象资源清理不完全导致内存泄漏;

  • 加锁解锁中抛异常

    加锁解锁的异常情况与new,delete的情况相似;

    在加锁与解锁中抛异常可能会导致死锁问题;

    同时这种情况都可以以RAII的方式处理该问题;


异常规范

请添加图片描述

异常规范是C++所引入的一个特性,用于声明一个函数可能抛出的异常类型,其基本语法为:

return_type function_name(parameters) throw(exception_type1, exception_type2, ...);

异常规范用于指定一个函数可以抛出的类型,可在函数声明中使用throw关键字后跟一个或多个异常类型;

若是保证这个函数将不会抛出任何异常可直接以return_type function_name(parameters) throw()的方式进行声明;

double Div(double num1, double num2)throw(runtime_error,int) {
  if (num2 == 0) {
    throw runtime_error("Division by zero error");
  }
  cout << "The process executed successfully" << endl;
  return num1 / num2;
}

int main() {
  try {
    Div(10,0);
  } catch (const runtime_error &e) {
    cout << "main handler a abonormal: " << e.what() << endl;
  }
  return 0;
}

在这个例子中对Div函数声明了可能会抛出runtime_error类型与int类型的异常,但实际上并不会抛出int类型的异常,这是因为编译器无法确认该类型的异常是否一定会被抛出;

但通常throw关键字是一个强制性的约定,当一种类型的异常未被throw声明但仍抛出了这个异常,那么即使在调用链中存在对应异常类型的catch时,该异常也不会被这个catch给捕获,而是会被程序以调用std::unexpected()的形式接收;

double Div(double num1, double num2)throw() {
  if (num2 == 0) {
    throw runtime_error("Division by zero error");
  }
  cout << "The process executed successfully" << endl;
  return num1 / num2;
}

int main() {
  try {
    Div(10,0);
  } catch (const runtime_error &e) {
    cout << "main handler a abonormal: " << e.what() << endl;
  }
  return 0;
}

在这个例子中Div函数声明了throw()关键字表示不会抛出任何异常,但实际上会抛出一个runtime_error类型的异常;

main函数中存在对应类型的catch捕获异常;

这个例子的运行结果为:

$ ./test 
terminate called after throwing an instance of 'std::runtime_error'
  what():  Division by zero error
Aborted

原因是当一个函数声明throw()但实际抛出异常时,程序不会进入任何catch块,这是因为编译器会假设这个函数不会抛出异常从而导致未定义行为;

这种情况下程序会被系统处理异常,通常表现为程序崩溃或终止;

C++11标准引入了noexcept关键字用来声明一个函数不会抛出任何异常,对应的该异常规范也逐渐开始被弃用,属于是一种过时的特性,;

  • noexcept 关键字

    在C++11标准中,引入了更为简洁和安全的机制来处理异常;

    该关键字通常声明一个函数不会抛出任何异常;

    void func() noexcept; // 表示该函数不会抛出异常
    

    该关键字的优势主要表现在于:

    • 性能

      使用noexcept使编译器可以进行更多的优化,因为该关键字可以假设此函数不会抛出任何异常;

    • 明确性

      noexcept的语义更加清晰,表示函数不会抛出异常而不需要列出所有可能的异常类型;

为了使得所有异常都被捕获可使用try{}catch(...){}来确保捕获了所有异常;

其中catch(...)则会捕获任何异常;

catch(...)可以捕获任何类型的异常并阻止他们向上传播;

double Div(double num1, double num2) {
  if (num2 == 0) {
    throw runtime_error("Division by zero error");
  }
  cout << "The process executed successfully" << endl;
  return num1 / num2;
}

void func() {
  try {
    Div(10, 0);
  } catch (...) { // 捕获所有异常并阻止异常向上传播
    cout << "未知异常" << endl;
  }
}

int main() {
  try {
    func();
  } catch (const runtime_error &e) {
    cout << "main handler a abonormal: " << e.what() << endl;
  }
  return 0;
}

在这个例子中Div函数将抛出一个异常,其中func函数将试图调用这个函数并以catch(...)的方式捕获该函数的异常,main函数将试图调用func函数,并catch(const runtime_error &e)捕获Div函数将会抛出的异常;

这个例子的运行结果为:

$ ./test 
未知异常

本质原因是异常被catch(...)所捕获并且阻止该异常向上传播导致main函数中的catch未正确捕获到异常;

通常catch(...)捕获应置于调用链的最上层(main函数),以确保所有的异常都被捕获;


异常的优缺点

请添加图片描述

  • 异常的优点

    • 清晰的错误处理逻辑

      异常处理使错误与正常逻辑分离,使得代码更易于阅读与维护;

    • 自动传播

      异常被抛出时将自动向上冒泡到调用栈中的上层函数,知道找到合适的catch块;

      这个机制避免了在每个函数中都需要检查错误并返回错误代码的繁琐;

    • 错误处理灵活

      使用异常处理可以针对不同类型的异常执行不同的处理逻辑;

      可通过定义多个catch块来处理不同类型的异常从而实现更加灵活和细致的错误处理;

    • 资源管理

      异常发生时可使用RAII(资源获取即初始化)模式来确保资源(内存,文件句柄等)的正确释放;

      即使发生异常,局部对象的析构函数仍会被调用从而避免资源泄露;

    • 简化的错误传递

      异常提供了一种简单的方式来传递错误信息,而不必通过返回值或全局变量;

      使得错误信息可以更加丰富与详细;

  • 缺点

    • 性能开销

      异常处理可能导致一定的性能开销,尤其是在异常频繁发生的情况下;

      虽然在正常情况下不会影响性能,但异常被抛出时堆栈展开和其他相关操作可能会变得十分昂贵;

    • 复杂性

      异常处理机制可能增加代码的复杂性,在多线程环境中异常的传播和处理可能会变得更加复杂;

      此外,过度使用异常可能导致代码难以理解和调试(执行流跳跃);

    • 难以追踪的异常

      异常的传播机制可能导致错误发生的地点与最终捕获异常的地点调用链相隔很远,使得定位和修复问题变得更加困难;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1982489.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

锂电池生产工艺数字化的业务架构.pptx

搜索《方案驿站》公众号进行下载。

【系统架构设计】数据库系统(五)

数据库系统&#xff08;五&#xff09; 数据库模式与范式数据库设计备份与恢复分布式数据库系统数据仓库数据挖掘NoSQL大数据 数据库模式与范式 数据库设计 备份与恢复 分布式数据库系统 数据仓库 数据挖掘 对数据挖掘技术进行支持的三种基础技术已经发展成熟&#xff0c…

【设计模式】六大原则-上

首先什么是设计模式&#xff1f; 相信刚上大学的你和我一样&#xff0c;在学习这门课的时候根本不了解这些设计原则和模式有什么用处&#xff0c;反而不如隔壁的C更有意思&#xff0c;至少还能弹出一个小黑框&#xff0c;给我个hello world。 如何你和我一样也是这么想&#xf…

2-56 基于matlab的图像融合增强技术

基于matlab的图像融合增强技术。通过原始图像——傅里叶变换——频率域滤波处理——傅里叶变换——增强后的图像。傅立叶变换以及傅立叶反变换.过程就是将空间的信息分解为在频率上的表示,通过傅立叶正反变换的处理,才使得频率域上的处理可以用于图像的增强。程序已调通&#x…

联想QuickFix工具中心,一款综合性电脑维护和管理工具

联想QuickFix工具中心是联想公司推出的一款综合性电脑维护和管理工具&#xff0c;它集成了众多实用的电脑维护工具&#xff0c;如系统优化、硬盘清理、网络优化、硬件诊断等&#xff0c;旨在为用户提供一个便捷的平台来解决电脑日常使用中遇到的各种问题。该工具中心适用于Wind…

AttributeError: ‘ChatGLMTokenizer‘ object has no attribute ‘sp_tokenizer‘. 已解决

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 ☁️运维工程师的职责&#xff1a;监…

【Linux---07】Shell脚本

文章目录 1. 前置说明1.1 创建shell脚本1.2 执行shell脚本1.3 调试shell脚本1.4 字符冲突 2. 变量2.1 创建&使用变量2.2 位置变量2.3 引号规则2.4 数值变量运算 3. 数组3.1 创建数组3.2 使用数组 4. 运算符4.1 比较运算符4.1.1 数字比较4.1.2 字符串比较 4.2 逻辑运算符4.3…

国内首个可调用API的视频模型,CogVideoX有多能打?

近期&#xff0c;智谱AI在其Maas开放平台(bigmodel.cn)发布了视频生成大模型CogVideoX&#xff0c;它提供了国内首个通过API来使用的文生视频和图生视频服务&#xff01; 话不多说&#xff0c;我们直接来看一下通过CogVideoX生成的一部短片。 技术原理 CogVideoX融合了文本、…

CSP-J复赛-模拟题4

1.区间覆盖问题&#xff1a; 题目描述 给定一个长度为n的序列1,2,...,a1​,a2​,...,an​。你可以对该序列执行区间覆盖操作&#xff0c;即将区间[l,r]中的数字,1,...,al​,al1​,...,ar​全部修改成同一个数字。 现在有T次操作&#xff0c;每次操作由l,r,p,k四个值组成&am…

未授权访问漏洞系列详解⑦!

VNC未授权访问漏洞 VNC 是虚拟网络控制台 Virtual Network Console 的英文缩写。它是一款优秀的远程控制工具软件由美国电话电报公司AT&T的欧洲研究实验室开发。VNC是基于 UNXI和 Linux 的免费开源软件由 VNC Server 和 VNC Viewer 两部分组成。VNC 默认端口号为 5900、590…

opencascade AIS_TypeFilter AIS_XRTrackedDevice源码学习

opencascade AIS_TypeFilter 前言 通过它们的类型选择交互对象。该过滤器会对本地上下文中的每个交互对象提出问题&#xff0c; 以确定它是否具有非空的所有者&#xff0c;并且如果是&#xff0c;则检查它是否是所需类型。 如果对象在每种情况下都返回 true&#xff0c;则保留…

运动控制卡——固高GTS

文章目录 前言什么是运动控制卡指示灯状态检测主卡指示灯状态说明端子板指示灯状态说明 软件调试(P39)何将控制器配置成脉冲模式设置与定位 编程C#编程 一些概念 前言 在一些控制多轴电机运动的场景下&#xff0c;除了需要驱动器驱动该轴的电机外&#xff0c;还需要用到控制卡…

kickstart自动部署

目录 一 准备工作二 安装软件及其相关配置配置步骤&#xff0c;图形引导部署web服务配置dhcp部署pxe 三 使用新机验证 一 准备工作 主机采用rhel7.9版本 本文使用图形化界面就行操作设置 取消VMware dhcp设置 yum group install "Server with GUI" init 5 #启动图…

vue3中使用logicFlow

浅结logicFlow使用&#xff1a; 应用场景&#xff1a;vue3中使用logicFlow绘制流程图 技术碎片应用&#xff1a; vue3&#xff1a;ref,reactive, onMounted, watchEffect,nextTick,inject logicFlow&#xff1a;节点&#xff0c;边&#xff0c;锚点&#xff0c;事件 官网&#…

大模型微调fine-tuning

版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhl大模型微调概述 大模型fine-tuning(微调)是一种重要的机器学习技术,特别是在处理自然语言处理(NLP)任务时广泛应用。它指在已经预训练好的大型深度学习模型基础上,使用新的、特定任务相关的…

【文献阅读】GraphAny: A Foundation Model for Node Classification on Any Graph

Abstract 可以执行任何新任务而无需特定训练的基础模型已经在视觉和语言应用中引发了机器学习的革命。然而&#xff0c;涉及图结构数据的应用仍然是基础模型面临的一个难题&#xff0c;因为每个图都有独特的特征和标签空间。传统的图机器学习模型&#xff0c;如图神经网络&…

在Fragment中显示高德地图

一、首先在高德官网上申请key 第一步:按照规定填写Key名称第二步:选择服务平台,运用在android上就勾选anroid就好了第三步:输入发布版安全码SHA1 首先打开命令行按住windows+r两个按键在命令行中输入cd .android,定位到.android文件下调试版本使用 debug.keystore,命令为…

交通预测数据文件梳理:METR-LA

文章目录 前言一、adj_METR-LA.pkl文件读取子文件1读取子文件2读取子文件3 二、METR-LA.h5文件 前言 最近做的实验比较多&#xff0c;对于交通预测数据的各种文件和文件中的数据格式理解愈加混乱&#xff0c;因此打算重新做一遍梳理来加深实验数据集的理解&#xff0c;本文章作…

数位dp学习

参考借鉴&#xff1a; 数位DP学习整理&#xff08;数位DP看完这篇你就会了&#xff09;-CSDN博客 AcWing1081.度的数量(数位DP)题解_求给定区间$ [x,y]$ 中满足下列条件的整数个数:这个数恰好等于 k k k 个互不相等-CSDN博客 就是类似前缀和的思想&#xff0c;进行数字在位数…

最新资讯 | 开源大模型Llama会失去技术优势吗?

昨夜&#xff0c;Meta宣布推出迄今为止最强大的开源模型——Llama 3.1 405B&#xff0c;支持上下文长度为128K Tokens&#xff0c;在基于15万亿个Tokens、超1.6万个H100 GPU上进行训练&#xff0c;这也是Meta有史以来第一次以这种规模训练Llama模型。Meta同时还发布了全新升级的…