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

C++矩阵库运算符重载语法困惑与内存泄漏问题咨询

矩阵库内存泄漏与赋值运算符问题解析

问题描述

我正在开发一个用于神经网络的矩阵运算库(不使用vector),目前遇到两个核心问题:

  1. 存在内存泄漏问题,尝试通过运算符重载解决,但赋值运算符在执行matrix<double> k = func(b);或循环中k = func(b);时无法正常工作。
  2. 想明确matrix& operator=(const matrix& other)与matrix<T> operator=(const matrix<T>& other)的区别。

以下是仅包含求和运算符的完整代码:

#include <iostream>

template <typename T>
class matrix{
public:
    T *data;
    int col;
    int row;
    int size;
    bool real;
    
    matrix(int col , int row){// declare matrix<T> r(40,40);
        this->data = new T[col * row];
        this->col = col;
        this->row = row;
        this->size = col * row;
        real = true;
    }
     matrix(){ // declare matrix<T> r;
        this->data = nullptr;
        this->col = -1;
        this->row = -1;
        this->size = -1;
     }
    void print(){
        for(int i=0; i<this->col; i++){
            for(int j=0; j<this->row; j++)
                std::cout << *(this->data + this->row * i + j ) << "  "; //note format is decided to *(data + i) cuz other format is confussing for me whenever 2d matrix. sry
            std::cout << std::endl;
        }
    }
    void operator=(const T& num){// fill
        for(int i=0; i<this->size; i++)
            *(this->data + i) = (T)num;
    }
    void operator=(matrix<T> other){ // mix
        if(other.real){ // copy
            col = other.col;
            row = other.row;
            size = other.size;
            data = new T[size];
            for(int i=0; i<size; i++)
                *(data + i) = *(other.data + i);
        }
        else{ //replace
            delete data;
            col = other.col;
            row = other.row;
            size = other.size;
            data = other.data;
        }
    }
    // SUM  
    matrix<T> operator+(const matrix<T>& other){
        if(this->real == 1 && other.real == 1){
            matrix<T> r(this->col, this->row);
            for(int i=0; i<this->size; i++)
                *(r.data + i) = *(this->data + i) + *(other.data + i);
            r.real = false;
            return r;
        }
        else if(this->real == 1 && other.real == 0){
            matrix<T> r = other;
            for(int i=0; i<this->size; i++)
                *(r.data + i) += *(this->data + i);
            r.real = false;
            return r;
        }
        else if(this->real == 0 && other.real == 1){
            matrix<T> *r = this;
            for(int i=0; i<this->size; i++)
                *(r->data + i) += *(this->data + i);
            r.real = false;
            return *r;
        }
        else{
            matrix<T> *r = this;
            for(int i=0; i<this->size; i++)
                *(r->data + i) += *(this->data + i);
            r.real = false;
            delete other.data;
            return *r;
        }
    }
};
template <typename T>
matrix<T> func(matrix<T> a){
    matrix<T> r = a;
    r.real = false; // while its false, gives error. also while its true, gives memory leak  
    r = 3;
    return r;
}



int main(){
    matrix<double> b(20,20);
    b = 2;
    matrix<double> k;
//  for(int i=0; i<1000000; i++)
    for(int i=0; i<10; i++)
        k = func(b);
    k.print();
    return 0;
}

一、两个赋值运算符的区别

  • matrix& operator=(const matrix& other):这是模板类成员函数的简化写法(模板类中matrix等价于matrix<T>),返回当前对象的引用,符合C++赋值运算符的标准规范——支持链式赋值(如a = b = c),同时避免不必要的对象拷贝。
  • matrix<T> operator=(const matrix<T>& other):这种写法返回对象副本,每次赋值都会创建新对象,效率低下且会引发额外内存管理问题,完全不符合赋值运算符的设计原则,绝对不能使用。

二、代码问题分析与修复

核心问题点

  1. 赋值运算符返回值错误:你的operator=返回void,无法支持链式赋值,且不符合C++标准约定,干扰了matrix<double> k = func(b);的拷贝初始化逻辑。
  2. 内存泄漏与野指针:
    • 当前对象已有data内存时,赋值运算符未释放旧内存就分配新内存,直接导致泄漏。
    • real标记的所有权逻辑混乱:real=false时直接接管other.data,但other是值传递参数,函数结束后会被销毁,引发野指针;delete other.data属于非法操作,会导致重复释放。
  3. 拷贝构造函数缺失:编译器自动生成的浅拷贝构造会让多个对象共享同一块data内存,销毁时引发重复释放崩溃。
  4. 析构函数缺失:未定义析构函数释放data内存,这是内存泄漏的核心原因之一。

修复后的代码

#include <iostream>
#include <cstddef>

template <typename T>
class matrix{
public:
    T* data;
    int col;
    int row;
    int size;

    // 空矩阵构造
    matrix() : data(nullptr), col(-1), row(-1), size(-1) {}

    // 指定大小矩阵构造
    matrix(int col, int row) : col(col), row(row), size(col * row) {
        data = new T[size](); // 初始化元素为默认值
    }

    // 拷贝构造:深拷贝
    matrix(const matrix<T>& other) : col(other.col), row(other.row), size(other.size) {
        data = new T[size];
        for (int i = 0; i < size; ++i) {
            data[i] = other.data[i];
        }
    }

    // 析构函数:释放内存
    ~matrix() {
        delete[] data;
        data = nullptr;
    }

    // 赋值运算符:拷贝交换范式,安全高效
    matrix<T>& operator=(matrix<T> other) {
        swap(*this, other);
        return *this;
    }

    // 友元交换函数
    friend void swap(matrix<T>& first, matrix<T>& second) noexcept {
        using std::swap;
        swap(first.data, second.data);
        swap(first.col, second.col);
        swap(first.row, second.row);
        swap(first.size, second.size);
    }

    // 填充单个值的赋值运算符
    matrix<T>& operator=(const T& num) {
        if (data == nullptr) {
            std::cerr << "Error: Cannot assign to empty matrix\n";
            return *this;
        }
        for (int i = 0; i < size; ++i) {
            data[i] = num;
        }
        return *this;
    }

    // 加法运算符:返回新矩阵,不修改原对象
    matrix<T> operator+(const matrix<T>& other) const {
        if (col != other.col || row != other.row) {
            std::cerr << "Error: Matrix dimensions mismatch for addition\n";
            return matrix<T>();
        }
        matrix<T> result(col, row);
        for (int i = 0; i < size; ++i) {
            result.data[i] = data[i] + other.data[i];
        }
        return result;
    }

    void print() const {
        for (int i = 0; i < col; ++i) {
            for (int j = 0; j < row; ++j) {
                std::cout << data[row * i + j] << "  ";
            }
            std::cout << "\n";
        }
    }
};

template <typename T>
matrix<T> func(matrix<T> a) {
    matrix<T> r = a; // 调用拷贝构造深拷贝
    r = 3; // 调用填充赋值运算符
    return r;
}

int main() {
    matrix<double> b(20, 20);
    b = 2;
    matrix<double> k;
    for (int i = 0; i < 10; ++i) {
        k = func(b); // 无内存泄漏
    }
    k.print();
    return 0;
}

关键修复说明

  1. 添加析构函数:用delete[]释放data数组,彻底解决内存泄漏。
  2. 实现拷贝构造函数:深拷贝data内存,避免多对象共享内存导致的重复释放。
  3. 赋值运算符采用拷贝交换范式:
    • 值传递参数自动触发拷贝构造,简化代码逻辑。
    • 交换当前对象与临时对象的资源,临时对象销毁时自动释放旧内存,保证异常安全。
  4. 移除real标记:所有权逻辑完全由拷贝构造和析构函数管理,消除野指针风险。
  5. 修正加法运算符:只返回新矩阵,不修改原对象;添加维度检查,避免非法运算。
  6. 规范返回值:所有赋值运算符返回matrix<T>&,支持链式赋值,符合C++标准。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 00:14:59