C++ Primer (第五版)-第十四章重载运算与类型转换

news2025/7/14 11:04:17

文章目录

  • 一、基本概念
    • 可以被重载
    • 某些运算符不应被重载
    • 尽量明智使用运算符重载
    • 赋值和复合赋值运算符
    • 选择作为成员或者非成员
  • 输入和输出运算符
    • 输入运算符尽量减少格式化操作
      • 输入输出运算符必须是非成员函数
    • 重载输入运算符>>
    • 输入时的错误
    • 标示错误
  • 算数和关系运算符
    • 相等运算符
    • 关系运算符
    • 赋值运算符
    • 下标运算符
    • 递增和递减运算符
      • 定义前置递增、递减运算符
      • 区分前置和后置运算符
    • 成员访问运算符->
    • 14.8 函数调用运算符
      • 含有状态的函数对象类
      • lambda是函数对象
      • 标准库定义的函数对象
      • 可调用对象与function
      • 不同类别可能具有相同都调用形式
    • 标准库function
      • 重载函数与function
  • 14.9 重载、类型转换与运算符
    • 类型转换运算符
      • 类类型转换成内置类型
      • 类类型转换为自定义类型
      • 转换为指针类型示例
      • 类型转换运算符可能产生意外结果
      • 显式类型转换运算符
      • 转为bool
      • 避免有二义性的类型转换
        • 第一种情况
        • 第二种情况
      • 重载函数与转换构造函数
      • 重载函数与用户定义的类型转换
    • 函数匹配与重载运算符

一、基本概念

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

可以被重载

在这里插入图片描述

某些运算符不应被重载

在这里插入图片描述

通常:不应该重载逗号、取地址、逻辑与/或运算符

尽量明智使用运算符重载

在这里插入图片描述

赋值和复合赋值运算符

在这里插入图片描述

选择作为成员或者非成员

在这里插入图片描述
在这里插入图片描述

输入和输出运算符

在这里插入图片描述

ostream &operator<<(ostream &os.const Sales_dataaa &item)
{
os<<item.isbn()<<""<<item.units_sold<<""<<item.revenue<<""<<item.avg_price();
return os;
}

输入运算符尽量减少格式化操作

通常输出运算符应该主要负责打印对象内容而非控制格式,输出运算符不应该打印换行符

输入输出运算符必须是非成员函数

在这里插入图片描述
在这里插入图片描述

重载输入运算符>>

istream &operator>>(istream &is,sales_data &item)
{
double price;//不需要初始化,因为我们将先读入数据到Price,之后使用
is>>item.bookNo>>item.units_sold>>price;
if(is)
  item.revenue=item.units_sold*price;
 else
  item.revenue=item.units_sold*price;
  return is;
}

在这里插入图片描述

输入时的错误

在这里插入图片描述

当读取操作发生错误时,输入运算符应该负责从错误中恢复。

标示错误

在这里插入图片描述

算数和关系运算符

如果类同时定义了算术运算符和相关的复合赋值运算符,则通常情况下应该使用复合赋值来实现算术运算符。

相等运算符

在这里插入图片描述

如果某个类在逻辑上有相等性都含义,则该类应该定义operator==.这样做可以使得用户更容易使用标准库算法来处理这个类

关系运算符

在这里插入图片描述

赋值运算符

在这里插入图片描述
在这里插入图片描述

下标运算符

标示容器的类通常可以通过元素在容器中的位置访问元素,这些类一般会定义下标运算符Operator[]

下标运算符必须是成员函数

在这里插入图片描述
在这里插入图片描述

class StrVec
{
    public:
       std::string& operator[]{std::size_t n}
       {return element[n];}
       const std::string& operator[](std::size_t n) const
       {return  elements[n];}
       private:
        std::string *elements;
};

在这里插入图片描述

递增和递减运算符

在这里插入图片描述

定义前置递增、递减运算符

Class StrBlobPtr
{
  Public:
         //递增和递减运算符
          StrBlobPtr& operator++(); //前置运算符
            StrBlobPtr& operator--(); //前置运算符
}

为了与内置版本保持一致,前置运算符应该返回递增或递减后对象的引用。
在这里插入图片描述

在这里插入图片描述

区分前置和后置运算符

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

成员访问运算符->

在迭代器和智能指针常常用到引用运算符和箭头运算符

class StrBlobPtr
{
    public: 
            std::string& operator*() const
            {
              autop p=check(curr,"dereference past end");
               return (*p)[curr];
               }
               std::string* operator->()const
               { 
               return  & this->operator*();
              }

}
#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <stdexcept>

// 前向声明StrBlobPtr类,以便在StrBlob中使用
class StrBlobPtr;

// StrBlob类:管理动态字符串数组的容器
class StrBlob {
    friend class StrBlobPtr;  // 允许StrBlobPtr访问私有成员
    
public:
    using size_type = std::vector<std::string>::size_type;
    
    // 默认构造函数:初始化空的vector
    StrBlob() : data(std::make_shared<std::vector<std::string>>()) {}
    
    // 带初始化列表的构造函数:用初始值列表初始化vector
    StrBlob(std::initializer_list<std::string> il) 
        : data(std::make_shared<std::vector<std::string>>(il)) {}
    
    // 返回容器大小
    size_type size() const { return data->size(); }
    
    // 判断容器是否为空
    bool empty() const { return data->empty(); }
    
    // 向容器尾部添加元素
    void push_back(const std::string &t) { data->push_back(t); }
    
    // 删除容器尾部元素(需检查容器非空)
    void pop_back();
    
    // 获取容器首元素的引用(非常量版本)
    std::string& front();
    
    // 获取容器首元素的引用(常量版本)
    const std::string& front() const;
    
    // 获取容器尾元素的引用(非常量版本)
    std::string& back();
    
    // 获取容器尾元素的引用(常量版本)
    const std::string& back() const;
    
    // 返回指向容器首元素的StrBlobPtr
    StrBlobPtr begin();
    
    // 返回指向容器尾后位置的StrBlobPtr
    StrBlobPtr end();
    
private:
    // 共享指针管理vector,允许多个StrBlob共享同一数据
    std::shared_ptr<std::vector<std::string>> data;
    
    // 检查索引是否合法,不合法则抛出异常
    void check(size_type i, const std::string &msg) const;
};

// 检查索引合法性,若不合法则抛出out_of_range异常
void StrBlob::check(size_type i, const std::string &msg) const {
    if (i >= data->size())
        throw std::out_of_range(msg);
}

// 返回首元素引用(非常量版本)
std::string& StrBlob::front() {
    check(0, "front on empty StrBlob");  // 检查容器非空
    return data->front();
}

// 返回首元素引用(常量版本)
const std::string& StrBlob::front() const {
    check(0, "front on empty StrBlob");  // 检查容器非空
    return data->front();
}

// 返回尾元素引用(非常量版本)
std::string& StrBlob::back() {
    check(0, "back on empty StrBlob");  // 检查容器非空
    return data->back();
}

// 返回尾元素引用(常量版本)
const std::string& StrBlob::back() const {
    check(0, "back on empty StrBlob");  // 检查容器非空
    return data->back();
}

// 删除尾元素
void StrBlob::pop_back() {
    check(0, "pop_back on empty StrBlob");  // 检查容器非空
    data->pop_back();
}

// StrBlobPtr类:StrBlob的智能指针/迭代器
class StrBlobPtr {
public:
    // 默认构造函数:初始化为未绑定状态
    StrBlobPtr() : curr(0) {}
    
    // 构造函数:绑定到指定StrBlob的指定位置
    StrBlobPtr(StrBlob &a, size_t sz = 0) : wptr(a.data), curr(sz) {}
    
    // 解引用操作符:返回当前位置的字符串引用
    std::string& operator*() const {
        auto p = check(curr, "dereference past end");  // 检查索引合法性
        return (*p)[curr];  // 返回vector中curr位置的字符串
    }
    
    // 箭头操作符:返回当前位置的字符串指针
    std::string* operator->() const { 
        return & this->operator*();  // 调用解引用操作符并返回地址
    }
    
    // 前置递增操作符:移动到下一个位置
    StrBlobPtr& operator++();
    
    // 前置递减操作符:移动到前一个位置
    StrBlobPtr& operator--();
    
    // 不等比较操作符:比较两个指针是否指向不同位置
    bool operator!=(const StrBlobPtr& rhs) const {
        return curr != rhs.curr;
    }
    
    // 赋值运算符:复制另一个StrBlobPtr的状态
    StrBlobPtr& operator=(const StrBlobPtr& rhs) {
        if (this != &rhs) {  // 避免自我赋值
            wptr = rhs.wptr;  // 复制弱引用(不增加引用计数)
            curr = rhs.curr;  // 复制当前位置
        }
        return *this;  // 返回自身引用,支持链式赋值
    }
    
    // assign函数:将指针重新绑定到指定StrBlob的指定位置
    StrBlobPtr& assign(StrBlob& sb, size_t pos = 0) {
        wptr = sb.data;  // 绑定到新StrBlob的data
        curr = pos;      // 设置新位置
        return *this;    // 返回自身引用,支持链式调用
    }
    
    // assign函数:复制另一个StrBlobPtr的状态
    StrBlobPtr& assign(const StrBlobPtr& rhs) {
        if (this != &rhs) {  // 避免自我赋值
            wptr = rhs.wptr;  // 复制弱引用
            curr = rhs.curr;  // 复制当前位置
        }
        return *this;  // 返回自身引用
    }
    
private:
    // 弱引用:指向StrBlob的底层vector,不控制其生命周期
    std::weak_ptr<std::vector<std::string>> wptr;
    
    // 当前位置索引
    size_t curr;
    
    // 检查底层vector是否存在且索引是否合法
    std::shared_ptr<std::vector<std::string>> 
        check(size_t, const std::string&) const;
};

// 检查底层vector是否存在且索引是否合法,返回shared_ptr
std::shared_ptr<std::vector<std::string>> 
StrBlobPtr::check(size_t i, const std::string &msg) const {
    auto ret = wptr.lock();  // 尝试获取shared_ptr(检查vector是否存在)
    if (!ret)
        throw std::runtime_error("unbound StrBlobPtr");  // vector已销毁
    if (i >= ret->size())
        throw std::out_of_range(msg);  // 索引越界
    return ret;  // 返回shared_ptr,确保在使用期间vector不会被销毁
}

// 前置递增操作符实现
StrBlobPtr& StrBlobPtr::operator++() {
    check(curr, "increment past end of StrBlobPtr");  // 检查当前位置合法
    ++curr;  // 移动到下一个位置
    return *this;  // 返回自身引用
}

// 前置递减操作符实现
StrBlobPtr& StrBlobPtr::operator--() {
    --curr;  // 移动到前一个位置
    check(curr, "decrement past begin of StrBlobPtr");  // 检查新位置合法
    return *this;  // 返回自身引用
}

// 返回指向StrBlob首元素的StrBlobPtr
StrBlobPtr StrBlob::begin() {
    return StrBlobPtr(*this);
}

// 返回指向StrBlob尾后位置的StrBlobPtr
StrBlobPtr StrBlob::end() {
    return StrBlobPtr(*this, data->size());
}

// 示例使用
int main() {
    // 创建StrBlob并初始化
    StrBlob sb = {"hello", "world", "!"};
    
    // 使用StrBlobPtr遍历StrBlob
    for (auto p = sb.begin(); p != sb.end(); ++p) {
        // 使用解引用操作符获取字符串
        std::cout << *p << std::endl;
        
        // 使用箭头操作符调用字符串的成员函数
        std::cout << p->size() << std::endl;
    }
    
    // 使用assign函数重新定位指针
    StrBlobPtr p(sb);
    p.assign(sb, 1);  // 指向第二个元素("world")
    std::cout << *p << std::endl;  // 输出: world
    
    return 0;
}

在这里插入图片描述

14.8 函数调用运算符

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如果类兴义了调用运算符,则该类的对象称作函数对象。因为可以调用这种对象,所以我们说这些对象的“行为像函数一样”

含有状态的函数对象类

#include <iostream>
#include <vector>
#include <algorithm>  // for std::for_each

class printstring {
private:
    std::ostream& os;
    char delim;

public:
    printstring(std::ostream& os, char delim = '\n') 
        : os(os), delim(delim) {}

    void operator()(const std::string& s) const {
        os << s << delim;
    }
};

int main() {
    std::vector<std::string> vs = {"hello", "world", "!"};
    
    // 使用for_each和printstring函数对象
    std::for_each(vs.begin(), vs.end(), printstring(std::cerr, '\n'));
    
    // 等价于以下循环
    for (const auto& s : vs) {
        std::cerr << s << '\n';
    }
    
    return 0;
}

lambda是函数对象

在这里插入图片描述
ppppppppp

#include <iostream>
#include <string>
#include <algorithm>
#include <vector>

class ShorterString {
public:
    bool operator()(const std::string &s1, const std::string &s2) const {
        return s1.size() < s2.size();
    }
};

int main() {
    std::vector<std::string> strings = {"apple", "banana", "pear", "kiwi"};
    auto min_str = std::min_element(strings.begin(), strings.end(), ShorterString());
    if (min_str != strings.end()) {
        std::cout << "The shortest string is: " << *min_str << std::endl;
    }
    return 0;
}

标准库定义的函数对象

在这里插入图片描述

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <functional>

int main() {
    std::vector<std::string> svec = {"apple", "banana", "cherry", "date"};
    // 使用greater<string>()作为比较函数对象进行排序
    std::sort(svec.begin(), svec.end(), std::greater<std::string>());
    for (const auto& str : svec) {
        std::cout << str << " ";
    }
    std::cout << std::endl;
    return 0;
}

可调用对象与function

在这里插入图片描述

不同类别可能具有相同都调用形式

#include <iostream>
#include <map>
#include <string>
#include <functional>

// 定义加法函数
int add(int a, int b) {
    return a + b;
}

// 定义减法函数
int subtract(int a, int b) {
    return a - b;
}

// 定义乘法函数
int multiply(int a, int b) {
    return a * b;
}

// 定义除法函数
int divide(int a, int b) {
    if (b == 0) {
        std::cerr << "除数不能为0" << std::endl;
        return 0;
    }
    return a / b;
}

int main() {
    // 使用std::map来实现函数表
    // 键是表示运算符的std::string对象,值是对应的函数对象(这里用std::function包装函数指针)
    std::map<std::string, std::function<int(int, int)>> functionTable;
    functionTable["+"] = add;
    functionTable["-"] = subtract;
    functionTable["*"] = multiply;
    functionTable["/"] = divide;

    int num1 = 10;
    int num2 = 2;
    std::string op = "/";

    // 从函数表中查找对应运算符的函数
    auto it = functionTable.find(op);
    if (it != functionTable.end()) {
        // 调用找到的函数进行计算
        int result = it->second(num1, num2);
        std::cout << num1 << " " << op << " " << num2 << " = " << result << std::endl;
    } else {
        std::cerr << "不支持的运算符" << std::endl;
    }

    return 0;
}

标准库function

function定义在functional头文件中。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

重载函数与function

在这里插入图片描述

在这里插入图片描述

#include <iostream>
#include <map>
#include <string>
#include <functional>

// 自定义类Sales_data(简单示例,假设只有一个数据成员price)
class Sales_data {
public:
    double price;
    Sales_data(double p) : price(p) {}
};

// 定义用于计算两个整数相加的函数
int add(int i, int j) {
    return i + j;
}

// 定义用于计算两个Sales_data对象相加的函数(这里简单将price相加)
Sales_data add(const Sales_data& s1, const Sales_data& s2) {
    return Sales_data(s1.price + s2.price);
}

int main() {
    // 定义一个map,键为string类型表示操作符,值为接受两个int参数并返回int结果的函数对象
    std::map<std::string, std::function<int(int, int)>> binops;

    // 以下插入操作会出错,因为存在函数重载,编译器不知道该用哪个add
    // binops.insert({" + ", add}); 

    // 修正方法一:使用lambda表达式明确指定插入的函数逻辑
    binops.insert({" + ", [](int a, int b) { return add(a, b); }});

    // 修正方法二:定义一个新的函数,明确调用int版本的add函数
    int newAdd(int a, int b) {
        return add(a, b);
    }
    binops.insert({" + ", newAdd});

    // 测试插入后的函数调用
    int result = binops["+"](3, 4);
    std::cout << "计算结果: " << result << std::endl;

    return 0;
}

14.9 重载、类型转换与运算符

类型转换运算符

  • 特殊成员函数:类型转换运算符是类里面特殊的成员函数,和普通成员函数不同,它没有显式写出来的返回类型(但实际有转换后的类型 ),也没有参数。
  • 转换功能:作用是把类类型的值转换为其他类型,这个其他类型可以是内置类型(像 int、double 等 ),也可以是自定义类型(比如其他类 ),但不能是 void ,也不能转换为数组或者函数类型,不过能转成指针(包括数组指针、函数指针 )或者引用类型。
  • const 限定:一般定义成 const 成员函数,因为转换过程通常不应该改变被转换对象本身的内容。

类类型转换成内置类型

#include <iostream>
#include <string>

class MyInt {
private:
    int value;
public:
    MyInt(int v) : value(v) {}
    // 类型转换运算符,将MyInt类型转换为int类型
    operator int() const {
        return value;
    }
};

int main() {
    MyInt num(5);
    int result = num + 3;  // 这里会自动调用operator int()将num转换为int类型
    std::cout << "结果: " << result << std::endl;
    return 0;
}

类类型转换为自定义类型

#include <iostream>
#include <string>

class SmallInt {
private:
    int smallValue;
public:
    SmallInt(int v) : smallValue(v) {}
    operator int() const {
        return smallValue;
    }
};

class BigInt {
private:
    int bigValue;
public:
    BigInt(int v) : bigValue(v) {}
    // 类型转换运算符,将BigInt类型转换为SmallInt类型
    operator SmallInt() const {
        // 这里简单处理,假设取bigValue的个位数作为SmallInt的值
        return SmallInt(bigValue % 10);
    }
};

int main() {
    BigInt big(25);
    SmallInt small = big;  // 这里会自动调用operator SmallInt()将big转换为SmallInt类型
    std::cout << "转换后的SmallInt值: " << static_cast<int>(small) << std::endl;
    return 0;
}

转换为指针类型示例

#include <iostream>

class MyClass {
private:
    int data;
public:
    MyClass(int d) : data(d) {}
    // 类型转换运算符,将MyClass类型转换为int*类型(这里只是示例,实际意义需根据场景确定)
    operator int*() const {
        return &data;
    }
};

int main() {
    MyClass obj(10);
    int* ptr = obj;  // 这里会自动调用operator int*()将obj转换为int*类型
    std::cout << "指针指向的值: " << *ptr << std::endl;
    return 0;
}

在这里插入图片描述

类型转换运算符可能产生意外结果

在这里插入图片描述

显式类型转换运算符

在这里插入图片描述

转为bool

在这里插入图片描述
在这里插入图片描述

避免有二义性的类型转换

在这里插入图片描述

第一种情况

假设我们有两个类 A 和 B ,以下代码展示了两个类提供相同类型转换导致的问题:

#include <iostream>

class B; // 前向声明

class A {
public:
    // 转换构造函数,接受B类对象来构造A类对象
    A(const B& b) {
        std::cout << "A的转换构造函数被调用" << std::endl;
    }
};

class B {
public:
    // 类型转换运算符,将B类对象转换为A类对象
    operator A() const {
        std::cout << "B的类型转换运算符被调用" << std::endl;
        return A(*this);
    }
};

void func(A a) {
    std::cout << "函数func接受A类对象" << std::endl;
}

int main() {
    B b;
    // 这里会产生二义性
    // 编译器不知道是该调用A的转换构造函数,还是B的类型转换运算符
    /// 调用时必须显式转换
//func(static_cast<A>(b)); // 明确指定使用B的转换运算符
    func(b); 
    return 0;
}

在这里插入图片描述

第二种情况

以一个自定义类 MyNumber 为例,假设它有多种与算术类型相关的转换规则,这可能会引发问题:

#include <iostream>

class MyNumber {
private:
    int value;
public:
    MyNumber(int v) : value(v) {}

    // 类型转换运算符,转换为int类型
    operator int() const {
        std::cout << "转换为int类型" << std::endl;
        return value;
    }
  // 只允许显式转换
   // explicit operator int() const { return value; }
   // explicit operator double() const { return value; }
    // 类型转换运算符,转换为double类型
    operator double() const {
        std::cout << "转换为double类型" << std::endl;
        return static_cast<double>(value);
    }
};

void func(int i) {
    std::cout << "函数func接受int类型参数: " << i << std::endl;
}

void func(double d) {
    std::cout << "函数func接受double类型参数: " << d << std::endl;
}

int main() {
    MyNumber num(5);
    // 这里会产生二义性
    // 编译器不知道是该将num转换为int还是double
    func(num); 
    return 0;
}

在这里插入图片描述

重载函数与转换构造函数

在这里插入图片描述

重载函数与用户定义的类型转换

显式转换
在这里插入图片描述
在这里插入图片描述

函数匹配与重载运算符

在这里插入图片描述

#include <iostream>
// 定义一个类
class MyClass {
private:
    int value;
public:
    MyClass(int v) : value(v) {}
    // 重载 + 运算符,作为成员函数
    MyClass operator+(const MyClass& other) const {
        return MyClass(value + other.value);
    }
};
// 重载 + 运算符,作为非成员函数
MyClass operator+(const MyClass& a, const MyClass& b) {
    return MyClass(a.value + b.value);
}
int main() {
    MyClass a(1);
    MyClass b(2);
    // 表达式 a + b ,这里编译器会考虑成员函数版本和非成员函数版本的 operator+
    MyClass result = a + b; 
    std::cout << "Result: " << result.value << std::endl;
    return 0;
}

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

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

相关文章

鸿蒙开发——5.ArkUI @Builder装饰器:打造高效可复用的UI组件

鸿蒙开发——5.ArkUI Builder装饰器&#xff1a;打造高效可复用的UI组件 ArkUI Builder装饰器&#xff1a;打造高效可复用的UI组件一、Builder装饰器是什么&#xff1f;二、两种构建函数类型1. 私有自定义构建函数2. 全局自定义构建函数 三、参数传递核心规则1. 按值传递&#…

PyTorchVideo实战:从零开始构建高效视频分类模型

视频理解作为机器学习的核心领域&#xff0c;为动作识别、视频摘要和监控等应用提供了技术基础。本教程将详细介绍如何利用PyTorchVideo和PyTorch Lightning两个强大框架&#xff0c;构建基于Kinetics数据集训练的3D ResNet模型&#xff0c;实现高效的视频分类流程。 PyTorch…

SEMI E40-0200 STANDARD FOR PROCESSING MANAGEMENT(加工管理标准)-(二)

8 行为规范 8.1 本章定义监督实体&#xff08;Supervisor&#xff09;与加工资源&#xff08;Processing Resource&#xff09;为实现物料加工所需的高层级通信逻辑&#xff0c;不涉及具体消息细节&#xff08;详见第10章消息服务&#xff09;。 8.2 加工任务通信 8.2.1 加工…

根据窗口大小自动调整页面缩放比例,并保持居中显示

vue 项目 直接上代码 图片u1.png 是个背景图片 图片u2.png 是个遮罩 <template><div id"app"><div class"viewBox"><divclass"screen":style"{ transform: translate(-50%,-50%…

Android SDK 国内镜像及配置方法(2025最新,包好使!)

2025最新android sdk下载配置 1、首先你需要有android sdk manager2、 直接上教程修改hosts文件配置域名映射即可(不用FQ)2.1 获取ping dl.google.com域名ip地址2.2 配置hosts文件域名映射2.3 可以随意下载你需要的sdk3、 总结:走过弯路,踩过坑!!!大家就不要踩了!避坑1…

【Python开源】深度解析:一款高效音频封面批量删除工具的设计与实现

&#x1f3b5; 【Python开源】深度解析&#xff1a;一款高效音频封面批量删除工具的设计与实现 &#x1f308; 个人主页&#xff1a;创客白泽 - CSDN博客 &#x1f525; 系列专栏&#xff1a;&#x1f40d;《Python开源项目实战》 &#x1f4a1; 热爱不止于代码&#xff0c;热情…

OpenStack Yoga版安装笔记(26)实例元数据笔记

一、实例元数据概述 1.1 元数据 &#xff08;官方文档&#xff1a;Metadata — nova 25.2.2.dev5 documentation&#xff09; Nova 通过一种叫做元数据&#xff08;metadata&#xff09;的机制向其启动的实例提供配置信息。这些机制通常通过诸如 cloud-init 这样的初始化软件…

【Linux】swap交换分区管理

目录 一、Swap 交换分区的功能 二、swap 交换分区的典型大小的设置 2.1 查看交换分区的大小 2.1.1 free 2.1.2 cat /proc/swaps 或 swapon -s 2.1.3 top 三、使用交换分区的整体流程 3.1 案例一 3.2 案例二 一、Swap 交换分区的功能 计算机运行一个程序首先会将外存&am…

VirtualBox 创建虚拟机并安装 Ubuntu 系统详细指南

VirtualBox 创建虚拟机并安装 Ubuntu 系统详细指南 一、准备工作1. 下载 Ubuntu 镜像2. 安装 VirtualBox二、创建虚拟机1. 新建虚拟机2. 分配内存3. 创建虚拟硬盘三、配置虚拟机1. 加载 Ubuntu 镜像2. 调整处理器核心数(可选)3. 启用 3D 加速(图形优化)四、安装 Ubuntu 系统…

触想CX-3588工控主板应用于移动AI数字人,赋能新型智能交互

一、行业发展背景 随着AI智能、自主导航和透明屏显示等技术的不断进步&#xff0c;以及用户对“拟人化”、“沉浸式”交互体验的期待&#xff0c;一种新型交互终端——“移动AI数字人”正在加速实现规模化商用。 各大展厅展馆、零售导购、教学政务甚至家庭场景中&#xff0c;移…

【深入浅出MySQL】之数据类型介绍

【深入浅出MySQL】之数据类型介绍 MySQL中常见的数据类型一览为什么需要如此多的数据类型数值类型BIT&#xff08;M&#xff09;类型INT类型TINYINT类型BIGINT类型浮点数类型float类型DECIMAL(M,D)类型区别总结 字符串类型CHAR类型VARCHAR(M)类型 日期和时间类型enum和set类型 …

Vue3响应式:effect作用域

# Vue3响应式: effect作用域 什么是Vue3响应式&#xff1f; 是一款流行的JavaScript框架&#xff0c;它提供了响应式和组件化的视图组织方式。在Vue3中&#xff0c;响应式是一种让数据变化自动反映在视图上的机制。当数据发生变化时&#xff0c;与之相关的视图会自动更新。 作用…

25.5.4数据结构|哈夫曼树 学习笔记

知识点前言 一、搞清楚概念 ●权&#xff1a;___________ ●带权路径长度&#xff1a;__________ WPL所有的叶子结点的权值*路径长度之和 ●前缀编码&#xff1a;____________ 二、构造哈夫曼树 n个带权值的结点&#xff0c;构造哈夫曼树算法&#xff1a; 1、转化成n棵树组成的…

RabbitMQ 深度解析:从核心组件到复杂应用场景

一.RabbitMQ简单介绍 消息队列作为分布式系统中不可或缺的组件&#xff0c;承担着解耦系统组件、保障数据可靠传输、提高系统吞吐量等重要职责。在众多消息队列产品中&#xff0c;RabbitMQ 凭借其可靠性和丰富的特性&#xff0c;在企业级应用中获得了广泛应用。 二.RabbitMQ …

【Linux笔记】系统的延迟任务、定时任务极其相关命令(at、crontab极其黑白名单等)

一、延时任务 1、概念 延时任务&#xff08;Delayed Jobs&#xff09;通常指在指定时间或特定条件满足后执行的任务。常见的实现方式包括 at 和 batch 命令&#xff0c;以及结合 cron 的调度功能。 2、命令 延时任务的命令最常用的是at命令&#xff0c;第二大节会详细介绍。…

使用阿里AI的API接口实现图片内容提取功能

参考链接地址&#xff1a;如何使用Qwen-VL模型_大模型服务平台百炼(Model Studio)-阿里云帮助中心 在windows下&#xff0c;使用python语言测试&#xff0c;版本&#xff1a;Python 3.8.9 一. 使用QVQ模型解决图片数学难题 import os import base64 import requests# base 64 …

从零开始搭建你的个人博客:使用 GitHub Pages 免费部署静态网站

&#x1f310; 从零开始搭建你的个人博客&#xff1a;使用 GitHub Pages 免费部署静态网站 在互联网时代&#xff0c;拥有一个属于自己的网站不仅是一种展示方式&#xff0c;更是一种技术能力的体现。今天我们将一步步学习如何通过 GitHub Pages 搭建一个免费的个人博客或简历…

C#串口通信

在C#中使用串口通信比较方便&#xff0c;.Net 提供了现成的类&#xff0c; SerialPort类。 本文不对原理啥的进行介绍&#xff0c;只介绍SerialPort类的使用。 SerialProt类内部是调用了CreateFile&#xff0c;WriteFile等WinAPI函数来实现串口通信。 在后期的Windows编程系…

服务器配置llama-factory问题解决

在配置运行llama-factory&#xff0c;环境问题后显示环境问题。这边给大家附上连接&#xff0c;我们的是liunx环境但是还是一样的。大家也记得先配置虚拟环境。 LLaMA-Factory部署以及微调大模型_llamafactory微调大模型-CSDN博客 之后大家看看遇到的问题是不是我这样。 AI搜索…

Spring Boot + Vue 实现在线视频教育平台

一、项目技术选型 前端技术&#xff1a; HTML CSS JavaScript Vue.js 前端框架 后端技术&#xff1a; Spring Boot 轻量级后端框架 MyBatis 持久层框架 数据库&#xff1a; MySQL 5.x / 8.0 开发环境&#xff1a; IDE&#xff1a;Eclipse / IntelliJ IDEA JDK&…