前段时间,我在网站上发现了一些实用函数的代码示例,这些示例函数在creating
,destructing
个对象时使用,或甚至在重载某些operators
时使用。
更确切地说,主要使用以下成员函数:init,copy,set和destroy。
init
成员函数用于初始化所有私有成员。它主要在constructors
内部调用,例如default
或parameter constructor
。 copy
成员函数用于执行作为deep copy
传递的对象的const reference
。它在reference constructor
内部和operator =
的重载内调用。 set
成员函数,主要allocates
内存为private members
需要它。 destroy
成员函数用于releasing
分配的内存。例如,它被称为destructor
。我想了解您的意见并知道这是一个很好的编程习惯吗?哪些好处或缺点是什么?欢迎任何意见和建议!
下面,我将说明如何为CMatrix<T>
类定义成员函数。
template < class T >
class CMatrix{
CMatrix(){ this->initMatrix(); }
CMatrix(int nRows, int nCols, int nChannels){
this->initComplexMatrix();
this->setComplexMatrix(nRows, nCols, nChannels);
}
CMatrix(const CMatrix<T> & refMatrix){
this->initComplexMatrix();
this->copyComplexMatrix(refMatrix);
}
CMatrix<T> & operator = (const CMatrix<T> & refMatrix){
if(this!=&refMatrix){
this->destroyComplexMatrix();
this->initComplexMatrix();
this->copyComplexMatrix(refMatrix);
}
return (*this);
}
T & CMatrix<T>::operator()(int, int, int);
T CMatrix<T>::operator()(int, int, int) const;
......
void initMatrix();
void copyMatrix(const CMatrix<T> & );
void setMatrix(int, int, int = 1);
void destroyMatrix();
......
~CMatrix(){ this->destroyMatrix(); }
private:
T *** m_pData;
int m_nRows;
int m_nCols;
int m_nChannels;
};
#include <matrix.h>
template < class T >
inline T & CMatrix<T>::operator()(int mrow, int mcol, int mchannel){
assert(mrow >= 0 && mrow < this->getRows());
assert(mcol >= 0 && mcol < this->getCols());
assert(mchannel >= 0 && mchannel < this->getChannels());
return this->m_pData[mrow][mcol][mchannel];
}
template < class T >
void CMatrix<T>::initMatrix(){
this->m_nRows = 0;
this->m_nCols = 0;
this->m_nChannels= 0;
this->m_pData = NULL;
}
template < class T >
void CMatrix<T>::copyMatrix(const CMatrix<T> & refMatrix){
if(refMatrix.m_pData!=NULL){
this->setMatrix(refMatrix.getRows(), refMatrix.getCols(), refMatrix.getChannels());
for(register int dy=0; dy < this->getRows(); dy++){
for(register int dx=0; dx < this->getCols(); dx++){
for(register int ch=0; ch < this->getChannels(); ch++){
this->m_pData[(dy)][(dx)][(ch)] = refMatrix.m_pData[(dy)][(dx)][(ch)];
}
}
}
}
else{
this->m_pData = NULL;
}
}
template < class T >
void CMatrix<T>::setMatrix(int nRows, int nCols, int nChannels){
this->destroyMatrix();
this->m_pData = NULL;
this->m_pData = new T ** [nRows];
for(register int dy=0; dy < nRows; dy++){
this->m_pData[dy] = NULL;
this->m_pData[dy] = new T * [nCols];
for(register int dx=0; dx < nCols; dx++){
this->m_pData[dy][dx] = NULL;
this->m_pData[dy][dx] = new T[nChannels];
}
}
this->setRows(mrows);
this->setCols(mcols);
this->setChannels(mchannels);
}
template < class T >
void CMatrix<T>::destroyMatrix(){
if(this->m_pData!=NULL){
for(register int dy=0; dy < this->getRows(); dy++){
for(register int dx=0; dx < this->getCols(); dx++){
delete [] this->m_pData[dy][dx];
}
delete [] this->m_pData[dy];
}
delete [] this->m_pData;
this->m_pData = NULL;
}
}
答案 0 :(得分:5)
不,不建议这样做。您提出的方式不是异常安全的,并且与需要非默认构造的const
或子对象不兼容。
而是使用 ctor-initializer-list 。代码重用可以通过 ctor-initializer-list 中调用的静态辅助函数或通过将逻辑移动到子对象构造函数来实现。
对于内存分配,请为每个资源使用一个子对象。内存管理逻辑最终在子对象构造函数和析构函数中。在许多情况下,您可以使用库中的现有RAII类,例如std::vector
,而不需要自己编写任何内存管理代码。
大多数运算符可以使用copy-and-swap idiom重用构造函数中的逻辑。
编辑:异常安全的构造可能看起来像这样:
#include <vector>
template<typename T>
class matrix
{
int m_nCols;
std::vector<T*> m_rows;
std::vector<T> m_cells;
size_t makeIndex( int row, int col ) const { return row*m_nCols + col; }
public:
matrix( int nRows, int nCols )
: m_nCols(nCols), m_rows(nRows), m_cells(nRows * nCols)
{
while (nRows--) m_rows[nRows] = &m_cells[nRows * nCols];
}
matrix( const matrix<T>& other )
: m_nCols(other.m_nCols), m_rows(other.m_rows.size()), m_cells(other.m_cells)
{
int nRows = other.m_rows.size();
while (nRows--) m_rows[nRows] = &m_cells[nRows * nCols];
}
void swap( matrix& other )
{
using std::swap;
swap(m_nCols, other.m_nCols);
swap(m_rows, other.m_rows);
swap(m_cells, other.m_cells);
}
matrix& operator=( matrix other )
{
other.swap(*this);
return *this;
}
const T& operator()( int row, int col ) const { return m_cells[makeIndex(row,col)]; }
T& operator()( int row, int col ) { return m_cells[makeIndex(row,col)]; }
};
std::vector
析构函数将负责释放内存,并且由于这两个分配是单独的对象,如果m_cells
无法分配其内存,m_rows
的析构函数将运行,什么都不会泄漏。
当然,std::vector
在这里有点过分,固定大小的数组RAII类就足够了。但std::auto_ptr
不能与数组一起使用。我认为C ++ 0x应该添加一个标准的固定大小的RAII数组类。
编辑:每个请求,一个三维版本:
#include <vector>
template<typename T>
class cube
{
int m_nCols, m_nRows;
std::vector<T> m_cells;
size_t makeIndex( int row, int col, int channel ) const { return (channel*m_nRows + row)*m_nCols + col; }
public:
cube( int nRows, int nCols, int nChannels )
: m_nCols(nCols), m_nRows(nRows), m_cells(nRows * nCols * nChannels)
{
}
cube( const cube<T>& other )
: m_nCols(other.m_nCols), m_nRows(other.m_nRows), m_cells(other.m_cells)
{
}
void swap( cube& other )
{
using std::swap;
swap(m_nCols, other.m_nCols);
swap(m_nRows, other.m_nRows);
swap(m_cells, other.m_cells);
}
cube& operator=( cube other )
{
other.swap(*this);
return *this;
}
const T& operator()( int row, int col, int channel ) const { return m_cells[makeIndex(row,col,channel)]; }
T& operator()( int row, int col, int channel ) { return m_cells[makeIndex(row,col,channel)]; }
class channel_iterator
{
cube& const cube;
int const row, col;
int channel;
friend class cube;
channel_iterator( cube& all, int r, int c, int n ) : cube(all), row(r), col(c), channel(n) {}
public:
T& operator*() const { return cube(row, col, channel); }
channel_iterator& operator++() { ++channel; return *this; }
channel_iterator operator++(int) { return channel_iterator(cube, row, col, channel++); }
bool operator!=(const channel_iterator& other) const { assert(&cube == &other.cube); return (row == other.row && col == other.col && channel == other.channel); }
};
int channel_count() const { return m_cells.size() / m_nRows / m_nChannels; }
pair<channel_iterator, channel_iterator> range(int row, int col) { return make_pair(channel_iterator(*this, row, col, 0), channel_iterator(*this, row, col, channel_count())); }
};
答案 1 :(得分:1)
实现赋值的更好模式是复制和交换习惯用法。
X& X::operator= (const X& rhv)
{
X copy(rhv); //reuse copy constructor
this->swap(copy); //reuse swap method - also useful for the user of the class!
//previously held resources automatically released by the destructor of copy
}
如果在复制期间发生异常,则按顺序执行必要的步骤以保持左侧值:在成功获取新资源之前不释放资源。
与您拥有的方法相比,优势在于交换方法不仅可以在内部实现类,还可以用于类的用户。在CMatrix类的情况下,实现将是:
void CMatrix<T>::swap(CMatrix<T>& other)
{
std::swap(m_pData, other.m_pData);
std::swap(m_nRows, other.m_nRows);
std::swap(m_nCols, other.m_nCols);
std::swap(m_nChannels, other.m_nChannels);
}
此外,如果合适,最好重用现有的RAII类,而不是在每个类中手动管理内存。