构造/析构/赋值运算

条款05 了解C++默默编写并调用哪些函数

  • 一个empty class编译器自动生成:default构造函数、copy构造函数、析构函数、copy赋值函数。

条款06 若不想使用编译器自动生成的函数,就该明确拒绝

所有编译器产生的函数都是public,为阻止这些函数被创建出来,你得自行声明它们。可以将copy构造函数或赋值操作符声明为private,并不予实现,既可以阻止编译器暗自创建它们,又可以成功阻止人们调用它。

  • 为阻止不要去自动生成函数功能,可将相应的成员函数声明为private并且不予实现。

条款07 为多态基类声明vritual析构函数

  • 带多态性质的基类应该声明一个vritual析构函数。如果class带有任何vritual函数,它就应该拥有一个vritual析构函数。
  • class的设计目的如果不是作为基类使用,或不是为了具备多态性,就不该声明vritual析构函数

当派生类对象经由一个基类指针被删除,而该基类带着一个non-vritual析构函数,会造成一个“局部销毁”对象,形成内存泄漏等问题。

欲实现出vritual函数,对象必须携带vptr信息,主要用来在运行期决定哪个vritual函数该被调用。vptr(vritual table pointer)指针。vptr只想一个由函数指针构成的数组,成为vtbl虚函数表;每个带有vritual函数的类都用一个对应的虚函数表。当对象调用某一vritual函数,实际被调用的函数取决于该对象的虚函数表指针所指的那个虚函数表。

条款08 别让异常逃离析构函数

  • 析构函数绝对不要吐出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕获任何异常,然后吞下它们或结束程序。
  • 如果客户需要对某个操作函数运行期抛出的异常做出反应,那么class应该提供一个普通函数(而非析构函数)执行该操作

条款09 绝不在构造和析构过程中调用vritual函数

  • 在构造和析构期间不要调用vritual函数,因为这类调用从不下降至派生类
class Transaction
{
public:
    Transaction();
    vritual void logTransaction() const =0;
    ...
};
Transaction::Transaction()
{
    ...
    logTransaction();
}

class BuyTransaction:public Transaction
{
public:
    vritual void logTransaction() const;
    ...
}
class SellTransaction:public Transaction
{
public:
    vritual void logTransaction() const;
    ...
}

此时,BuyTransaction b;
这时候调用的logTransaction函数是基类的版本。因为基类构造函数先执行。

解决方法,在基类中将logTransaction函数改为non-vritual函数,然后要求派生类构造函数传递必要信息给Transaction构造函数,而后那个构造函数便可安全地调用non-vritual的logTransaction函数

class Transaction
{
public:
    explicit Transaction(const std::string& logInfo);
    vritual void logTransaction(const std::string& logInfo) const;//如今是个non-vritual函数
    ...
};
Transaction::Transaction(const std::string& logInfo)
{
    ...
    logTransaction(logInfo);
}

class BuyTransaction:public Transaction
{
public:
    BuyTransaction(parameters)
    :Transaction(createLogString(parameters)){...}//将log信息传给基类构造函数
    ...
private:
    static std::string createLogString(parameters);
};

注意本例派生类中的私有static函数createLogString(parameters),比起在成员初始化列表内给予积累所需数据,利用辅助函数创建一个值给基类构造函数往往比较方便和可读。

条款10 令operator=返回一个reference to *this
为实现“连锁赋值”,重载复制操作符必须返回一个reference指向操作符的左侧实参。

class Widget
{
public:
    Wiget& operator=(const Widget& rhs)//返回类型是个引用,指向当前对象
    {
    ...
    return* this;//返回左侧对象
    }
}

条款11 在operator= 中处理“自我赋值”

  • 确保当对象自我赋值是opreator=有良好行为。其中技术包括比较“来源对象”和“目标对象”的地址、精心周到的语句顺序、以及copy-and-swap
    一个正确的做法
class Bitmap{...};
class Widget
{
...
void swap(Widget& rhs);//交换*this和rhs的数据
...
private:
    Bitmap* pd;
};
Widget& Widget::operator=(const Widget& rhs)
{
    Widget temp(rhs);//为rhs数据制作一份副本
    swap(temp);//将*this数据和上述副本的数据交换
    return *this;
}

条款12 复制对象勿忘其每一个成分

  • copying函数应该确保复制“对象内的所有成员变量”以及“所有基类成分”
  • 不要尝试以某个copying函数实现另一个copying函数。应该将共同机能放进第三个函数中,并由两个copying函数共同调用。

设计良好的面向对象程序会将对象的内部封装起来,只留两个函数负责对象拷贝,copy构造函数和重载赋值操作符,我们称它们为copying函数。

重载复制操作符调用copy构造函数是不合理的,因为这就像试图构造一个已经存在的对象。
反过来,copy构造函数调用复制操作符同样无意义。构造函数用来初始化对象,而复制操作符只施行于已初始化对象身上。

转载于:https://www.cnblogs.com/Blackmanba-xzl/p/5340276.html

好的,下面是一个基于动态数组的矩阵类的实现,包括类的构造析构、拷贝、赋值等基本操作,以及矩阵的按元素加减乘除运算,矩阵的乘法、转置和求逆等运算: ```c++ #include <iostream> #include <vector> using namespace std; class Matrix { private: int row; int col; vector<vector<double>> data; public: // 构造函数 Matrix(int r, int c, double val = 0) { row = r; col = c; data.resize(row, vector<double>(col, val)); } // 拷贝构造函数 Matrix(const Matrix &other) { row = other.row; col = other.col; data = other.data; } // 析构函数 ~Matrix() {} // 赋值运算符 Matrix &operator=(const Matrix &other) { if (this != &other) { row = other.row; col = other.col; data = other.data; } return *this; } // 矩阵按元素加法 Matrix operator+(const Matrix &other) const { if (row != other.row || col != other.col) { throw "矩阵加法维度不匹配!"; } Matrix result(row, col); for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { result.data[i][j] = data[i][j] + other.data[i][j]; } } return result; } // 矩阵按元素减法 Matrix operator-(const Matrix &other) const { if (row != other.row || col != other.col) { throw "矩阵减法维度不匹配!"; } Matrix result(row, col); for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { result.data[i][j] = data[i][j] - other.data[i][j]; } } return result; } // 矩阵按元素乘法 Matrix operator*(const Matrix &other) const { if (row != other.row || col != other.col) { throw "矩阵乘法维度不匹配!"; } Matrix result(row, col); for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { result.data[i][j] = data[i][j] * other.data[i][j]; } } return result; } // 矩阵按元素除法 Matrix operator/(const Matrix &other) const { if (row != other.row || col != other.col) { throw "矩阵除法维度不匹配!"; } Matrix result(row, col); for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { if (other.data[i][j] == 0) { throw "矩阵除法除数不能为0!"; } result.data[i][j] = data[i][j] / other.data[i][j]; } } return result; } // 矩阵乘法 Matrix operator*(const Matrix &other) const { if (col != other.row) { throw "矩阵乘法维度不匹配!"; } Matrix result(row, other.col); for (int i = 0; i < row; i++) { for (int j = 0; j < other.col; j++) { for (int k = 0; k < col; k++) { result.data[i][j] += data[i][k] * other.data[k][j]; } } } return result; } // 矩阵转置 Matrix transpose() const { Matrix result(col, row); for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { result.data[j][i] = data[i][j]; } } return result; } // 矩阵求逆 Matrix inverse() const { if (row != col) { throw "非方阵不可求逆!"; } int n = row; Matrix A(*this); Matrix B(n, n); B.eye(); for (int k = 0; k < n; k++) { double max = abs(A.data[k][k]); int index = k; // 找到最大元素所在的行 for (int i = k + 1; i < n; i++) { if (abs(A.data[i][k]) > max) { max = abs(A.data[i][k]); index = i; } } // 如果最大元素为0,则该矩阵不可逆 if (max == 0) { throw "该矩阵不可逆!"; } // 交换第k行和第index行 if (index != k) { for (int j = 0; j < n; j++) { swap(A.data[k][j], A.data[index][j]); swap(B.data[k][j], B.data[index][j]); } } // 使A[k][k]为1 double d = A.data[k][k]; for (int j = 0; j < n; j++) { A.data[k][j] /= d; B.data[k][j] /= d; } // 使A[k][j](j!=k)为0 for (int i = k + 1; i < n; i++) { double d = A.data[i][k]; for (int j = 0; j < n; j++) { A.data[i][j] -= d * A.data[k][j]; B.data[i][j] -= d * B.data[k][j]; } } } // 使A[j][k](j!=k)为0 for (int k = n - 1; k > 0; k--) { for (int i = k - 1; i >= 0; i--) { double d = A.data[i][k]; for (int j = 0; j < n; j++) { A.data[i][j] -= d * A.data[k][j]; B.data[i][j] -= d * B.data[k][j]; } } } return B; } // 单位矩阵 void eye() { if (row != col) { throw "非方阵不可转化为单位矩阵!"; } for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { data[i][j] = (i == j) ? 1 : 0; } } } // 输出矩阵元素 void print() const { for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { cout << data[i][j] << "\t"; } cout << endl; } } }; ``` 在这个矩阵类中,我们使用了动态二维数组来存储矩阵的数据。在构造函数中,我们使用了resize()函数来动态分配内存。在拷贝构造函数和赋值运算符中,我们直接复制了矩阵的行列数和数据,而不是使用浅拷贝。 在矩阵的按元素加减乘除运算中,我们首先检查了两个矩阵的维度是否匹配,然后按照对应元素相加减乘除的方式计算结果,并返回一个新的矩阵对象。 在矩阵的乘法运算中,我们首先检查了两个矩阵的维度是否匹配,然后按照矩阵乘法的定义计算结果,并返回一个新的矩阵对象。 在矩阵的转置和求逆运算中,我们使用了一些常见的线性代数算法来实现。需要注意的是,在求逆运算中,我们使用了高斯-约旦消元法来求解矩阵的逆矩阵,该算法的时间复杂度为O(n^3)。 最后,我们还实现了一个输出矩阵元素的函数print(),用于调试和测试。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值