用于类构造函数、析构函数和运算符重载的实用函数
不久前,我在一个网站上发现了一些实用函数的代码示例,这些函数在创建
、析构
对象时使用,甚至在重载某些运算符<时使用。 /代码>。 更准确地说,主要使用以下成员函数:init、copy、set和destroy。
init
成员函数用于初始化所有私有成员。它主要在构造函数
内部调用,例如默认
或参数构造函数
。copy
成员函数用于对作为const 引用
传递的对象进行深层复制
。它在引用构造函数
和operator =
的重载内部调用。set
成员函数主要为需要它的私有成员
分配内存。- 最后,
destroy
成员函数用于释放
分配的内存。例如,它在析构函数
内部被调用。
我想听听您的意见并知道这是否是一个好的编程实践?有哪些优点或缺点?欢迎任何意见和建议! 下面,我将说明如何为 CMatrix
类定义这些成员函数。
矩阵.h
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;
};
矩阵.cpp
#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;
}
}
A while ago, I found in a website some code examples of utility functions that are used when creating
, destructing
objects, or even when overloading some of their operators
.
More precisely, the following member functions are mainly used: init, copy, set, and destroy.
- The
init
member function is used to initialize all the private members. It's mostly called inside theconstructors
, e.g. thedefault
orparameter constructor
. - The
copy
member function is used to do adeep copy
of an object passed as aconst reference
. It is called inside thereference constructor
and the overload of theoperator =
. - The
set
member function which mainlyallocates
memory for theprivate members
that require it. - Finally, the
destroy
member function is used forreleasing
the allocated memory. It's called, for example, inside of thedestructor
.
I would like to have your opinion and know if this is a good programming practice? Which are the benefits or drawbacks? Any comments and suggestions are welcomed!
Below, I'm illustrating how those member functions are defined for a CMatrix<T>
class.
matrix.h
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;
};
matrix.cpp
#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;
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
不,不建议这样做。您建议的方式不是异常安全的,并且与 const 或需要非默认构造的子对象不兼容。
请改用ctor-initializer-list。代码重用可以通过在构造函数初始化列表中调用的静态辅助函数或将逻辑移至子对象构造函数中来实现。
对于内存分配,每个资源使用一个子对象。内存管理逻辑最终出现在子对象构造函数和析构函数中。在许多情况下,您可以使用库中现有的 RAII 类,例如 std::vector,而无需自己编写任何内存管理代码。
大多数运算符可以使用 copy-and-swap 惯用法重用构造函数中的逻辑。
编辑:异常安全构造可能看起来像这样:
std::vector 析构函数将负责释放内存,并且由于两个分配是单独的对象,如果 m_cells > 无法分配内存,
m_rows
的析构函数将运行并且不会泄漏任何内容。当然,std::vector 在这里有点矫枉过正,固定大小的数组 RAII 类就足够了。但是 std::auto_ptr 不能与数组一起使用。我认为 C++0x 应该添加一个标准的固定大小 RAII 数组类。
编辑:根据请求,3D 版本:
No, this is not recommended. The way you propose is not exception safe and is not compatible with
const
or subobjects that need non-default construction.Instead use the ctor-initializer-list. Code reuse can be achieved through static helper functions called in the ctor-initializer-list or by moving logic into subobject constructors.
For memory allocation, use a subobject per resource. The memory management logic ends up in the subobject constructor and destructor. In many cases, you can use existing RAII classes from the library, such as
std::vector
, and not need to write any memory management code yourself.Most operators can reuse the logic in the constructors, using the copy-and-swap idiom.
EDIT: Exception-safe construction might look something like this:
The
std::vector
destructor will take care of freeing the memory, and since the two allocations are separate objects, ifm_cells
fails to allocate its memory, the destructor form_rows
will run and nothing will leak.Of course,
std::vector
is a little bit overkill here, a fixed-size array RAII class would be sufficient. Butstd::auto_ptr
can't be used with arrays. I think C++0x is supposed to add a standard fixed-size RAII array class.EDIT: Per request, a 3-D version:
实现分配的更好模式是复制和交换习惯用法。
如果复制过程中发生异常,这将按照保持左侧值不变的顺序执行必要的步骤:在成功获取新资源之前不会释放资源。
与您所拥有的方法相比,交换方法的优点是,交换方法不仅对于内部实现类有用,而且对于类的用户也有用。对于 CMatrix 类,实现将是:
另外,如果合适的话,最好重用现有的 RAII 类,而不是在每个类中手动管理内存。
A better pattern for implementing assignment is the copy and swap idiom.
This performs the necessary steps in an order that keeps left-hand value unchanged if an exception occurs during the copying: doesn't release resources before new resources have been successfully obtained.
And the advantage over the methods you have is that the swap method is useful not only internally for implementing the class, but also for the user of the class. In case of the CMatrix class the implementation would be:
Also, if suitable, it is a better idea to reuse existing RAII classes instead of managing memory manually in each and every class.