2010-04-23 9 views
5

Yo, como muchos programadores antes que yo, me estoy arrancando el pelo escribiendo el derecho de paso-matriz-clase-en-C++. Nunca he hecho una sobrecarga al operador muy grave y esto está causando problemas. Esencialmente, al pasar por¿Cómo hago para sobrecargar a los operadores de C++ para permitir el encadenamiento?

Esto es lo que llamo causar los problemas.

cMatrix Kev = CT::cMatrix::GetUnitMatrix(4, true); 
    Kev *= 4.0f; 
    cMatrix Baz = Kev; 
    Kev = Kev+Baz; //HERE! 

Lo que parece estar sucediendo de acuerdo con el depurador es que se añaden Kev y Baz pero entonces se pierde el valor y cuando se trata de reasignar a Kev, la memoria es sólo sus valores por defecto poco fiables. ¿Cómo sobrecargo mis operadores para permitir esta declaración? Mi código (reducido) está debajo.

//header 
class cMatrix 
{ 
private: 
    float* _internal; 
    UInt32 _r; 
    UInt32 _c; 
    bool _zeroindexed; 

    //fast, assumes zero index, no safety checks 
    float cMatrix::_getelement(UInt32 r, UInt32 c) 
    { 
     return _internal[(r*this->_c)+c]; 
    } 

    void cMatrix::_setelement(UInt32 r, UInt32 c, float Value) 
    { 
     _internal[(r*this->_c)+c] = Value; 
    } 

public: 
    cMatrix(UInt32 r, UInt32 c, bool IsZeroIndexed); 
    cMatrix(cMatrix& m); 
    ~cMatrix(void); 

    //operators 
    cMatrix& operator + (cMatrix m); 
    cMatrix& operator += (cMatrix m); 
    cMatrix& operator = (const cMatrix &m); 
}; 

//stripped source file 
cMatrix::cMatrix(cMatrix& m) 
{ 
    _r = m._r; 
    _c = m._c; 
    _zeroindexed = m._zeroindexed; 
    _internal = new float[_r*_c]; 

    UInt32 size = GetElementCount(); 

    for (UInt32 i = 0; i < size; i++) 
    { 
     _internal[i] = m._internal[i]; 
    } 
} 

cMatrix::~cMatrix(void) 
{ 
    delete[] _internal; 
} 
cMatrix& cMatrix::operator+(cMatrix m) 
{ 
    return cMatrix(*this) += m; 
} 

cMatrix& cMatrix::operator*(float f) 
{ 
    return cMatrix(*this) *= f; 
} 

cMatrix& cMatrix::operator*=(float f) 
{ 
    UInt32 size = GetElementCount(); 

    for (UInt32 i = 0; i < size; i++) 
    { 
     _internal[i] *= f; 
    } 

    return *this; 
} 

cMatrix& cMatrix::operator+=(cMatrix m) 
{ 
    if (_c != m._c || _r != m._r) 
    { 
     throw new cCTException("Cannot add two matrix classes of different sizes."); 
    } 
    if (!(_zeroindexed && m._zeroindexed)) 
    { 
     throw new cCTException("Zero-Indexed mismatch."); 
    } 

    for (UInt32 row = 0; row < _r; row++) 
    { 
     for (UInt32 column = 0; column < _c; column++) 
     { 
      float Current = _getelement(row, column) + m._getelement(row, column); 
      _setelement(row, column, Current); 
     } 
    } 

    return *this; 
} 

cMatrix& cMatrix::operator=(const cMatrix &m) 
{ 
    if (this != &m) 
    { 
     _r = m._r; 
     _c = m._c; 
     _zeroindexed = m._zeroindexed; 

     delete[] _internal; 

     _internal = new float[_r*_c]; 

     UInt32 size = GetElementCount(); 

     for (UInt32 i = 0; i < size; i++) 
     { 
      _internal[i] = m._internal[i]; 
     } 
    } 
    return *this; 
    } 

Respuesta

10

Sus operadores + y * deben devolver por valor, no por referencia. Estás devolviendo una variable temporal por referencia. Además, estás argumentos se pasan por valor, cuando debería ser una referencia constante:

cMatrix cMatrix::operator+(cMatrix const& m) 
{ 
    cMatrix matrix(*this); 
    matrix += m; 
    return matrix; 
} 

cMatrix cMatrix::operator*(float f) 
{ 
    cMatrix matrix(*this); 
    matrix *= m; 
    return matrix; 
} 

debería echar un vistazo a Boost.Operators. Esto le permitiría implementar solo operator*= y operator+= y proporcionar automáticamente las implementaciones correctas para operator+ y operator*.

PS: Si implementa la clase matriz sólo por la experiencia de aprendizaje, no dudan en buscar otras implementaciones como el Matrix Template Library.

PPS: Si no desea utilizar impulso, o si simplemente quiere entender la mejor práctica, echar un vistazo a Boost.Operator y hacer lo que hacen.

+0

Mi objetivo en Esta etapa consiste en seguir con la versión estándar de C++ y evitar vincular cualquier otra librería si es posible. ¡Boost es útil, pero también un poco bestial! – User2400

+1

Prefiero implementar 'operator +' como no miembro. Entre otras ventajas, esto también permite que un compilador C++ 11 optimice la copia de un valor rf t argumento si lo pasa por valor. (Consulte mi respuesta.) – sbi

+1

@fneep: Entiendo eso, pero aún debe echar un vistazo a Boost.Operator para comprender la forma canónica de implementar operadores. – Sebastian

8

OMI la forma canónica de sobrecargar Además es la siguiente:

class X { 
public: 
    X& operator+=(const X& rhs) { /*add rhs to *this*/ } 
}; 

inline X operator+(X lhs, const X& rhs) {lhs+=rhs; return lhs;} 

Lo mismo vale para -, *, /, donde sea aplicable.

Tenga en cuenta que + devuelve una copia, no una referencia. Eso es importante, porque A+B crea un nuevo valor, por lo que no puede devolver una referencia a uno existente.
Además, es una función gratuita. OMI es mejor implementar aquellos de los operadores binarios que pueden implementarse como miembro o como una función libre como funciones libres, si tratan sus operandos simétricamente (como lo hace +), y como funciones miembro, si tratan sus operandos asimétricamente (como +=, que cambia su argumento izquierdo. Si implementa operator+ como miembro, usted tendrá que hacer la función const (X operator+(const X& rhs) const), de modo que pueda ser invocada para los elementos constantes en el lado izquierdo.

+0

que es exactamente lo que haría Boost.Operator. – Sebastian

+1

@Sebastion: Sí, pero IMO debe aprender cómo hacerlo primero, antes de tomar atajos usando boost.De lo contrario, ¿qué vas a hacer si haces algo mal con '+ =' y el compilador escupe un horrible mensaje de error en tu cara apuntando hacia el fuelle de boost? Esta es una implementación _right-of-passage_, después de todo. – sbi

+0

Y creo que una buena forma de aprender es verificar una implementación canónica. – Sebastian

Cuestiones relacionadas