You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++ operator<<输出异常修复及std::variant替换union方案

问题根因

代码输出异常由4个核心问题导致:

  • 无参构造函数未初始化length成员,对象创建时length为内存残留的随机垃圾值;同时无参构造仅为selling分配1字节堆空间,后续直接strcpy长字符串触发堆缓冲区溢出,属于未定义行为,直接导致首行乱码。
  • 手动调用strcpy给selling赋值后,未同步更新length成员值,重载的operator<<调用os.write(s.selling, s.length)时,会按照错误的长度读取内存,将不属于字符串的垃圾数据一并输出。
  • 原带参构造函数中存在多余的std::cout << s << "\n";语句,会在对象构造时自动打印字符串,后续循环的printf也会再次输出商品名,造成重复打印。
  • 类使用裸指针管理堆内存,但未实现拷贝构造、拷贝赋值函数,默认生成的浅拷贝逻辑会导致重复释放内存的崩溃风险。
修复后完整代码(已集成std::variant替换自定义联合体)
#include <iostream>
#include <variant>
#include <cstring>
#include <cstdio>

using namespace std;

class Product {
public:
    char* selling;
    unsigned length;
    float unitPrice;
    variant<int, float> amount; // 用类型安全的variant替换原自定义联合体

    // 无参构造函数
    Product();
    // 带参构造函数
    Product(const char* s, float unitPrice, variant<int, float> amt);
    // 拷贝构造函数(遵循Rule of Three,避免浅拷贝问题)
    Product(const Product& other);
    // 拷贝赋值运算符
    Product& operator=(const Product& other);
    ~Product() { delete[] selling; }

    unsigned len() const;
    Product& operator+= (const Product& s);
    char& operator[](size_t idx);
    char operator[](size_t idx) const;

    friend std::ostream& operator<<(std::ostream& os, const Product& s) {
        os.write(s.selling, s.length);
        return os;
    }

    int size() { return length; }

    // 封装名称设置方法,避免手动strcpy导致的溢出、length不同步问题
    void setName(const char* s) {
        if (s == nullptr) {
            delete[] selling;
            selling = new char[1]{'\0'};
            length = 0;
            return;
        }
        delete[] selling;
        length = strlen(s);
        selling = new char[length + 1];
        strcpy(selling, s);
    }
};

// 无参构造实现,初始化所有成员
Product::Product() : selling{new char[1] {'\0'}}, length{0}, unitPrice{0.00f}, amount{0} {}

// 带参构造实现,删除原多余的cout打印逻辑
Product::Product(const char* s, float unitPrice, variant<int, float> amt) : unitPrice(unitPrice), amount(amt) {
    if (s == nullptr) {
        selling = new char[1]{'\0'};
        length = 0;
        this->unitPrice = 0.00f;
    } else {
        length = strlen(s);
        selling = new char[length + 1];
        strcpy(selling, s);
    }
}

// 拷贝构造实现
Product::Product(const Product& other) {
    length = other.length;
    selling = new char[length + 1];
    strcpy(selling, other.selling);
    unitPrice = other.unitPrice;
    amount = other.amount;
}

// 拷贝赋值实现
Product& Product::operator=(const Product& other) {
    if (this == &other) return *this;
    delete[] selling;
    length = other.length;
    selling = new char[length + 1];
    strcpy(selling, other.selling);
    unitPrice = other.unitPrice;
    amount = other.amount;
    return *this;
}

unsigned Product::len() const {
    return length;
}

Product& Product::operator+=(const Product& s) {
    unsigned newLen = length + s.length;
    char* str = new char[newLen + 1];
    for (unsigned j = 0; j < length; j++) str[j] = selling[j];
    for (unsigned i = 0; i < s.length; i++) str[length + i] = s.selling[i];
    str[newLen] = '\0';
    delete[] selling;
    length = newLen;
    selling = str;
    return *this;
}

Product operator+(const Product& lhs, const Product& rhs) {
    Product rv(lhs);
    rv += rhs;
    return rv;
}

char& Product::operator[](size_t idx) { return selling[idx]; }
char Product::operator[](size_t idx) const { return selling[idx]; }

// variant访问器,自动匹配库存类型打印对应信息
struct InventoryPrinter {
    void operator()(int units) const {
        printf("We have %d units for sale\n", units);
    }
    void operator()(float kgs) const {
        printf("We have %f kgs for sale\n", kgs);
    }
};

int main() {
    int numItems = 2;
    int i;
    Product dieselMotorBike;
    Product apples;

    dieselMotorBike.setName("A Diesel Motor Cycle");
    dieselMotorBike.unitPrice = 5488.00f;
    dieselMotorBike.amount = 4; // variant自动识别存储int类型
    cout << dieselMotorBike << "\n";

    apples.setName("Granny duBois");
    apples.unitPrice = 0.78f;
    apples.amount = 0.5f; // variant自动识别存储float类型
    cout << apples << "\n";

    Product* myEbayStore[2];
    myEbayStore[0] = &dieselMotorBike;
    myEbayStore[1] = &apples;
    for (i = 0; i < numItems; i++) {
        printf("\n%s\n", myEbayStore[i]->selling);
        // 用visit安全访问variant值,不需要手动switch判断类型
        visit(InventoryPrinter{}, myEbayStore[i]->amount);
    }

    return 0;
}
std::variant替换说明

相比自定义联合体,std::variant是类型安全的,不需要额外维护unitType这类标记字段记录当前存储的类型:

  • 定义variant<int, float> amount作为库存成员后,赋值时会自动匹配存储类型:传入整数自动存int,传入浮点数自动存float。
  • 访问值时使用std::visit,传入重载了对应类型operator()的访问器即可,编译器会在编译期检查是否覆盖了所有可能的类型,不会出现自定义联合体类型访问不匹配的未定义行为。
  • 如果需要判断当前存储的类型,可以调用amount.index(),返回0代表存储的是第一个类型int,返回1代表存储的是第二个类型float;也可以用holds_alternative<int>(amount)直接判断当前是否存储int值。

内容的提问来源于stack exchange,提问作者Hector Ta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 00:12:16