头像

uruubito




离线:4天前


最近来访(70)
用户头像
克里斯保罗
用户头像
那你呢
用户头像
努力的草鱼
用户头像
叁笠
用户头像
梦在深巷
用户头像
强盛集团董事长
用户头像
时来天地皆同力
用户头像
奥卡姆剃刀
用户头像
Violet_66
用户头像
北海没有WA
用户头像
好き
用户头像
刘家豪@2021
用户头像
Rabilista
用户头像
阿多米尼斯多雷特
用户头像
placeboooo
用户头像
xiaogege
用户头像
yxc的小迷妹
用户头像
SY_8
用户头像
the.midnight
用户头像
富士山下梨花道旁


类型转换运算符(type conversion operator)是一种C++的特殊运算符,它可以将一个对象转换为另一种类型。所有的类型转换运算符都以operator关键字开头,然后是要转换的类型,例如operator int()表示将某个对象转换为int类型。

常见的类型转换运算符包括:

operator bool():将对象转换为布尔型
operator int():将对象转换为整型
operator double():将对象转换为双精度浮点型
operator string():将对象转换为字符串型
等等

在程序中,可以通过将一个对象强制转换为另一种类型,调用类型转换运算符。例如:

class MyInt {
public:
    MyInt(int val) : m_val(val) {}
    operator double() { return static_cast<double>(m_val); }  // 类型转换运算符,将MyInt对象转换为双精度浮点型
private:
    int m_val;
};

int main()
{
    MyInt i(42);
    double d = static_cast<double>(i);  // 调用类型转换运算符,将i对象转换为double类型
    return 0;
}

在上面的例子中,由于MyInt类实现了类型转换运算符operator double(),可以将MyInt对象i强制转换为double类型,并将结果赋值给d。

为什么需要类型转换运算符?

类型转换是将一个数据类型转换为另一个数据类型的过程。在实际编程中,我们通常需要将数据从一种类型转换为另一种类型。以下是一些常见的需要类型转换的场景:

  1. 数据类型不匹配:在程序中,往往需要执行不同类型之间的运算,例如整形和浮点型的运算、字符型和整形的运算等。此时需要将不同类型的数据转换为相同类型,才能进行运算。

  2. 函数参数类型:函数的参数类型可能与调用函数时传入的实参类型不匹配,这时需要将实参类型转换为参数类型,才能调用函数。

  3. 类型兼容:有时我们需要将一个对象转换为另外一个类的对象,这种情况通常需要通过类型转换实现。

C++ 提供了多种类型转换方式:

  • 隐式转换:编译器会自动将不同类型之间的数据进行转换,例如将整型赋值给浮点型变量时,自动将整型转换为浮点型。
  • 显式转换:程序员可以直接对变量进行类型转换,这种类型转换称为显式转换,也称强制类型转换。
  • 类型转换函数:类型转换函数是一个类的成员函数,用于将类类型的数据转换为其他类型的数据。

需要注意的是,在类型转换过程中应该遵循一定的规则,以避免出现运行时错误。因此,在编写程序时需要慎重使用类型转换,并且应该在转换前仔细检查数据类型的匹配情况。



分享 段错误

段错误 (Segmentation fault) 是一种硬件或软件引起的程序错误,在 C/C++/Assembly 等编程语言中经常出现。它通常会导致程序崩溃或异常退出。

一般情况下,段错误由于代码访问了不属于该程序代码段的内存地址,或者访问了已经被释放的内存地址。其本质上是对内存访问的越界错误。

内存泄漏是指分配的内存空间没有被释放而导致一段时间后系统内存不足,甚至可能导致崩溃和程序异常。

为了避免发生内存泄漏和段错误,开发者可以采取以下措施:

  1. 访问内存之前检查内存地址是否合法。在访问数组等结构时,需要检查下标是否越界。在指针操作中,需要确保指针非空,指向的内存空间存在。

  2. 及时释放不再使用的内存。在使用 new 分配内存时,必须使用 delete 释放;使用 malloc 分配内存时,必须使用 free 释放。如果程序中需要大量动态分配内存,建议使用智能指针等内存智能管理工具。

  3. 使用内存检测工具。一些内存泄漏与段错误检测工具,如 Valgrind,可以自动分析程序代码的内存问题,并提示开发者修复错误。

  4. 避免多线程带来的内存问题。在多线程程序中,可能会存在多个线程访问同一块内存的情况,需要对内存的读写进行互斥控制,以保证程序的正确执行。

  5. 使用编译器的编译选项开启编译器的错误检查机制。这样可以让编译器在编译时就能检查出一些可疑的内存访问错误。

以上措施可以帮助程序开发者有效地避免内存泄漏和段错误等问题,提升程序质量和运行稳定性。



分享 消息队列

uruubito
11天前

消息队列是指一种消息传递的机制,它将消息存储在一个队列中,并提供了读取和写入队列的接口。在实际应用中,消息队列可以用来解耦网站后台系统中的不同模块,提高不同模块之间的协作效率,如生产者-消费者、异步任务、消息通知等场景。当然,消息队列同样可以在网络通信中使用,以实现不同进程之间的消息传递。

消息队列的主要应用场景包括:

  1. 异步通信:消息队列除了是单向通信还支持异步通信。具体实现时就是发送消息后不会立刻得到响应,而是继续处理自己的业务逻辑,等待对方响应。

  2. 系统解耦:不同系统模块之间用消息队列进行通信,解耦不同模块之间的直接调用, 避免模块间的直接耦合。

  3. 消息通知:消息队列可以在某个事件发生时向形成订阅的接收者推送消息,如实时通讯中,一方向服务器发送消息,服务器推送给在线的另一方。

  4. 流量削峰:消息队列可以在高并发请求涌入时,将请求放入队列中,然后逐一处理,从而避免系统瞬间被服务器压垮。

综上,消息队列在分布式系统、高并发网络中扮演着重要的角色,也成为了不同业务场景下常见的解决方案之一。

进程间通信(IPC)是计算机中两个或多个进程交换数据或信息的一种机制。消息队列是一种进程间通信的方式,它可以让不同的进程之间在不共享内存的情况下进行通信。

具体地,消息队列由一个消息队列标识符(即消息队列的唯一标识符)和存放在系统内部的消息缓冲区组成。进程可以通过消息队列标识符来访问消息队列,并通过系统调用向消息队列中写入和读取消息。

消息队列通信的步骤通常如下:

  1. 创建消息队列:通过 system call msgget 创建一个新的消息队列,并分配一个消息队列标识符。

  2. 发送消息:进程通过 system call msgsnd 向指定消息队列发送一条消息。在发送时,消息需要指定类型和长度。每个消息都包括消息类型字段,可以通过这个字段对消息进行分类管理。

  3. 接收消息:进程通过 system call msgrcv 从指定消息队列中接收一条消息。在接收消息时,进程可以选择特定类型的消息进行接收,也可以接收队列第一条消息。

  4. 销毁消息队列:在消息队列不再需要时,进程可以使用 system call msgctl 进行删除或销毁。

需要注意的是,消息队列有着较大的缓存空间,能够存储大批数据,但频繁的发送和读取会导致系统性能下降。因此,我们可以采用多线程和轮询方式来减少轮询的时间和内存空间的消耗。

总之,消息队列是一种简单而可靠的 IPC 机制,通过它可以实现不同进程之间的消息交换。但其在性能上可能存在一定的局限性,需要在使用时注意控制。



分享 内存映射

uruubito
12天前

内存映射(Memory Mapping)是指将存储在硬盘中的文件映射到进程的地址空间中,使得进程可以通过内存的方式来对文件进行访问。内存映射可以看作是一种虚拟内存技术,是现代操作系统中常用的一种 I/O 操作方式。

内存映射的工作机制是将文件的部分或全部内容映射到虚拟内存空间,从而实现对文件的访问。映射后,就可以通过对虚拟内存的访问,就可以实现对文件的读写。在内存映射中,文件的字节在物理磁盘上仍然存在,但通过内存映射,进程可以通过地址访问这些字节,就像它们在内存中一样。

内存映射有以下几个优点:

  1. 降低 I/O 的复杂性,提高性能:当使用内存映射时,操作系统会将文件的部分或全部内容读入内存中,并将文件映射到内存中的一段地址空间中。这样,应用程序就可以像访问内存一样来读写文件,这减少了 I/O 操作的次数,提高了程序的性能。

  2. 减少了数据复制:使用内存映射可以减少数据复制的过程。当需要对文件进行读取时,不需要将数据从磁盘读入内存,然后再拷贝到应用程序的缓冲区中,而是直接将文件映射到内存,这样就避免了数据复制的过程。

  3. 更加简单的代码实现:使用内存映射可以让代码实现更加简单,因为不需要自己来实现 I/O 操作,而是通过操作内存来实现读写文件。

总的来说,内存映射是一种高效的文件 I/O 操作方式,可以提高程序的性能,减少数据复制,以及使得代码实现更加简单。在对大规模数据进行读写的场景下,内存映射是一种非常有效的工具。



分享 git

uruubito
12天前
git add
git commit -m
git push -u origin


分享 vscode

uruubito
15天前
{
    "configurations": [
        {
            "name": "C/C++: g++ 生成和调试活动文件",
            "type": "cppdbg",
            "request": "launch",
            "program": "${fileDirname}/${fileBasenameNoExtension}",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${fileDirname}",
            "environment": [],
            "externalConsole": true,
            "miDebuggerArgs": "-q -ex quit; wait() { fg >/dev/null; }; /bin/gdb -q --interpreter=mi",

            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "为 gdb 启用整齐打印",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                },
                {
                    "description": "将反汇编风格设置为 Intel",
                    "text": "-gdb-set disassembly-flavor intel",
                    "ignoreFailures": true
                }
            ],
            "preLaunchTask": "C/C++: g++ 生成活动文件",
            "miDebuggerPath": "/usr/bin/gdb"
        },
        {
            "name": "C/C++: g++-11 生成和调试活动文件",
            "type": "cppdbg",
            "request": "launch",
            "program": "${fileDirname}/${fileBasenameNoExtension}",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${fileDirname}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "为 gdb 启用整齐打印",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                },
                {
                    "description": "将反汇编风格设置为 Intel",
                    "text": "-gdb-set disassembly-flavor intel",
                    "ignoreFailures": true
                }
            ],
            "preLaunchTask": "C/C++: g++-11 生成活动文件",
            "miDebuggerPath": "/usr/bin/gdb"
        }
    ],
    "version": "2.0.0"
}
{
    "tasks": [
        {
            "type": "cppbuild",
            "label": "C/C++: g++ 生成活动文件",
            "command": "/usr/bin/g++",
            "args": [
                "-fdiagnostics-color=always",
                "-g",
                "${file}",
                "-o",
                "${fileDirname}/${fileBasenameNoExtension}"
            ],
            "options": {
                "cwd": "${fileDirname}"
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "detail": "调试器生成的任务。"
        },
        {
            "type": "cppbuild",
            "label": "C/C++: g++-11 生成活动文件",
            "command": "/usr/bin/g++-11",
            "args": [
                "-fdiagnostics-color=always",
                "-g",
                "${file}",
                "-o",
                "${fileDirname}/${fileBasenameNoExtension}"
            ],
            "options": {
                "cwd": "${fileDirname}"
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": "build",
            "detail": "调试器生成的任务。"
        }
    ],
    "version": "2.0.0"
}


分享 投票算法

uruubito
16天前

现将任意元素设为当前选举人
遍历数组,维护一个count,初始值为0;
当当前元素与被选举元素相等,count++,
不相等的时候,count–,当count为0的
时候将当前元素推为当前选举元素,有点蒙
意思是在一个数组中随机抽走两个元素,
剩下的一定是众数


class Solution {
public:
    int majorityElement(vector<int>& nums) {
        int count=1;
        int mode=nums[0];
        for(int i=1;i<nums.size();i++)
        {
            if(count==0)mode=nums[i];
            if(nums[i]==mode)count++;
            else count--;
        }
        return mode;
    }
};

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        int cnt=0,mm;
        unordered_map<int,int> qq;
        for(auto x:nums)
        {
            qq[x]++;
            if(qq[x]>cnt)
            {
                mm=x;
                cnt=qq[x];
            }
        }
        return mm;
    }
};


分享 题目

uruubito
16天前

QQ截图20230314130824.png

class MinStack {
public:
        stack<int> st;
        stack<int> minS;
    MinStack() {
         while(!st.empty())
         {
             st.pop();
         }
         while(!minS.empty())
         {
             minS.pop();
         }
    }

    void push(int val) {
        st.push(val);
        if(minS.empty())
            minS.push(val);
        else
            minS.push(min(minS.top(),val));
    }

    void pop() {
        st.pop();
        minS.pop();
    }

    int top() {
        return st.top();
    }

    int getMin() {
        return minS.top();
    }
};



分享 format

uruubito
16天前

C20中新增了一个std::format库,它提供了格式化输出字符串的能力,类似于Python中的format字符串,本质上是一个字符串模板工具。与传统C的字符串拼接、printf相比,format字符串既灵活又安全,避免了很多BOF等安全问题,也让字符串拼接的语法更加简单易用。

下面是一些std::format库的使用示例:

使用std::format输出字符串:

#include <format>
#include <iostream>

int main() {
    std::string str = "hello";
    int num = 23;
    auto output = std::format("str: {}, num: {}", str, num);
    std::cout << output << std::endl;
    return 0;
}
str: hello, num: 23

在std::format中使用占位符,例如:

#include <format>
#include <iostream>

int main() {
    int num = 23;
    auto output = std::format("int value: {0:d}, hex value: {0:x}, binary value: {0:b}", num);
    std::cout << output << std::endl;
    return 0;
}
int value: 23, hex value: 17, binary value: 10111

其中,{0:d}表示使用十进制格式输出,{0:x}表示使用十六进制格式输出,{0:b}表示使用二进制格式输出。这种方式非常简洁、灵活、易于理解。

std::format 还支持带宽度和精度的格式化输出:

#include <format>
#include <iostream>

int main() {
    double num = 3.1415926;
    auto output = std::format("the pi number: {0:.3f}", num);
    std::cout << output << std::endl;
    return 0;
}
the pi number: 3.142

其中,:.表示精度,:3表示宽度。

需要注意的是,std::format目前只有C20标准支持,需要在编译器指定C20标准,例如:

g++ main.cpp -std=c++20



uruubito
16天前
#include <iostream>
#include <string>
#include <vector>
#include <mutex>
#include <condition_variable>   //线程相关的头文件

#include <mysql/mysql.h>

// 定义一个数据库连接类
class DBConnection {
public:
    DBConnection()
        : mysql_(nullptr), is_use_(false) {}
    ~DBConnection() { Close(); }   //析构函数,关掉数据库连接

    // 连接数据库
    bool Connect(const std::string& host, uint16_t port, const std::string& user, const std::string& password, const std::string& database) {
        mysql_ = mysql_init(nullptr);   //初始化mysql对象
        if (mysql_real_connect(mysql_, host.c_str(), user.c_str(), password.c_str(), database.c_str(), port, nullptr, 0) == nullptr) {
            return false;
        }
        return true;
    }

    // 关闭数据库连接
    void Close() {
        if (mysql_ != nullptr) {
            mysql_close(mysql_);
            mysql_ = nullptr;
        }
    }

    //获取连接状态
    bool IsUse() const { return is_use_; }
    //设置连接状态
    void SetUse(bool is_use) { is_use_ = is_use; }
    //获取mysql对象
    MYSQL* GetMySQL() const { return mysql_; }

private:
    MYSQL* mysql_;  //mysql结构体
    bool is_use_;   //是否被占用
};

// 定义一个数据库连接池类
class DBConnectionPool {
public:
    DBConnectionPool(size_t max_size, const std::string& host, uint16_t port, const std::string& user, const std::string& password, const std::string& database)
        : max_size_(max_size), host_(host), port_(port), user_(user), password_(password), database_(database) {}

    // 初始化数据库连接池
    bool Init() {
        for (size_t i = 0; i < max_size_; ++i) {
            DBConnection conn;
            if (!conn.Connect(host_, port_, user_, password_, database_)) {    //连接失败
                return false;
            }
            conns_.push_back(std::move(conn));  //将连接对象push_back到容器中
        }
        return true;
    }

    // 获取数据库连接,加锁
    DBConnection* GetConnection() {
        std::unique_lock<std::mutex> lock(mutex_);   //加锁
        while (true) {
            for (auto& conn : conns_) {    //遍历连接池
                if (!conn.IsUse()) {      //如果没有被占用,尝试ping mysql服务器
                    if (mysql_ping(conn.GetMySQL()) == 0) {   //ping成功
                        conn.SetUse(true);      //标记该连接正在使用
                        return &conn;
                    } else {      //ping失败
                        conn.Close();   //关闭连接
                        if (conn.Connect(host_, port_, user_, password_, database_)) {   //重新连接数据库
                            conn.SetUse(true);
                            return &conn;
                        }
                    }
                }
            }
            cond_.wait(lock);   //连接池中没有可用的连接,等待
        }
    }

    // 释放数据库连接,解锁
    void ReleaseConnection(DBConnection* conn) {
        std::unique_lock<std::mutex> lock(mutex_);    //加锁
        conn->SetUse(false);   //标记该连接没有使用
        cond_.notify_one();    //唤醒一个等待连接的线程
    }

private:
    std::vector<DBConnection> conns_;   //连接池容器
    size_t max_size_;                   //最大连接数
    std::string host_;                  //数据库地址
    uint16_t port_;                     //数据库端口
    std::string user_;                  //数据库用户名
    std::string password_;              //数据库密码
    std::string database_;              //数据库名称
    std::mutex mutex_;                  //线程互斥互斥量
    std::condition_variable cond_;      //线程条件变量
};

int main() {
    DBConnectionPool pool(5, "localhost", 3306, "root", "password", "test_db");
    pool.Init();   //数据库连接池初始化

    DBConnection* conn = pool.GetConnection();   //获取数据库连接
    MYSQL* mysql = conn->GetMySQL();             //获取MySQL对象
    mysql_query(mysql, "SELECT * from test_table");   //执行MySQL语句
    MYSQL_RES* result = mysql_store_result(mysql);   //获取查询结果
    MYSQL_ROW row;
    while ((row = mysql_fetch_row(result)) != nullptr) {
        std::cout << row[0] << " " << row[1] << std::endl;
    }
    mysql_free_result(result);   //释放结果集

    pool.ReleaseConnection(conn);   //释放数据库连接

    return 0;
}