构造/析构/赋值运算(二)

构造/析构/赋值运算

Constructors,Destructors,and Assignment Operators

9.绝不在构造和析构过程中调用virtual函数

Never call virtual functions during construction or destruction.
derived class对象内的base class成分会在derived class自身成分被构造之前先构造妥当。

假如base class内含有虚函数,在base class成分构造期间,derived class成分还未初始化。如果此期间调用的virtual函数下降到derived class阶层,derived class的函数几乎必然取用local变量,而那些变量尚未初始化。这是一张通往不明确行为和彻夜调试大会的直达车票。

对待这种情景,最安全的做法认为,对象是在derived class构造函数开始执行前不会成为derived class。

无法使用virtual函数从base class 向下调用,在构造期间,我可以“领derived class将必要的构造信息向上传递至base class构造函数”替换之加以弥补。

#include<iostream>
class Transaction {
public:
	explicit Transaction(const std::string& logInfo);
	void logTransaction(const std::string& logInfo) const;//non-virtual

};
Transaction::Transaction(const std::string& logInfo)
{
	logTransaction(logInfo);//non-virtual调用
}
class BuyTransaction :public Transaction {
public:
	BuyTransaction(/*parameters*/)
		:Transaction(createLogString(/*parameters*/)) //将log信息传递给base class
	{}
private:
	static std::string createLogString(/*parameters*/);//static函数先于对象存在
};
  • 在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived class(比起当前执行构造和析构的函数那层)。

10.令operator=返回一个reference to *this

Have assignment operators return to *this.
这是个协议,并非强制性。这样的好处是,方便连续赋值。适用于所有赋值相关运算。

#include<iostream>
class Ration {
public:
	Ration(int val_=0)
		:val(val_)
	{
	}

	Ration& operator=(const Ration& rhs)
	{
		this->val = rhs.val;
		return *this;//这个协议实用于+=、-=、*=等
	}
private:
	int val;
};
int main()
{
	Ration r1(15);
	Ration r2, r3;
	r2 = r3 = r1;//赋值采用右结合律,等同于r2 = (r3 = r1)
}

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

Handle assignment to self in operator=.
可能是自我赋值的场景

a[i]=a[j];
*px=*py;

这些不明显的自我赋值,均是由“别名”带来的结果,所谓“别名”就是“有一个以上的方法指代某对象”,如指针and引用,以及继承中的切片行为带来的结果。

class Base {};
class Derived :public Base {};
void do_some_thing(Base& b, Derived* d);//此时b和d可能指向同一个对象

证同测试

class Bitmap{};
class Widget {
private:
	Bitmap* pb;
public:
	Widget& operator=(const Widget& rhs)
	{
		if (&rhs == this)return *this;//证同测试

		delete pb;
		pb = new Bitmap(*rhs.pb);
		return *this;
	}
};

上述传统解决方法仍然不能解决异常方面的麻烦。如果在赋值过程抛出异常,那么对象将指向被删除的Bitmap,这样的指针十分害人。

class Bitmap{};
class Widget {
private:
	Bitmap* pb;
public:
	Widget& operator=(const Widget& rhs)
	{
		Bitmap* pOrig = pb;
		pb = new Bitmap(*rhs.pb);
		delete pOrig;
		return *this;
	}
};

如果“自我赋值”出现的概率很大,那么,最好再加上“证同测试”,以提高效率,否则这样就可以解决问题。
CAS,copy and swap

class Bitmap{};
class Widget {
private:
	void swap(Widget&rhs)//交换两个Widget的pb指向
	{
	}
	Bitmap* pb;
public:
	Widget& operator=(Widget rhs)
	{
		swap(rhs);
		return *this;
	}
};
  • 确保当对象自我赋值时operator=有良好行为。其中技术包括比较“来源对象”和“目标对象”的地址、精心周到的语句顺序、以及copy-and-swap。
  • 确定任何函数如果操作一个以上的对象,而其中多个对象是同一个对象时,其行为仍然正确。

12.复制对象时勿忘其每一个成分

Copy all part of an object
除了保证复制所有的local成员变量.任何时候只要你承担起“为derived class撰写copying函数”的重责大任,必须很小心第也复制其base class成分。那些成分往往是private,所以你无法直接访问她们,你应该让derived class的copying函数调用base class函数。

其实两个copying函数往往拥有相同的实现本体,这可能会引诱你让某个函数去怂恿另一个函数以避免代码重复.这样精益求精的态度值得赞赏,但是令某个copying函数调用一个copying函数却无法让你达到你想要的目标.

令copy assignment操作符调用copy构造函数是不合理的,因为这就像试图构造一个已经存在的对象.这挺起来就很不通顺.单纯的接受这个建议:你不该令copy assignment操作符调用copy构造函数.

令copy构造函数调用copy assignment操作符同样没有意义.构造函数是用来初始化新对象,而assignent操作符只施行于已初始化对象身上.对应尚未构造好的对象赋值,就像在一个尚未初始化的对象身上做"只对已初始化对象才有意义"的事情一样.同样无聊.

如果你发现你的copy构造函数和copy assignment操作符有相近的代码,消除重复代码的做法就是建立一个新的成员函数给两者调用这样的函数往往是private而且常被命名为init. 你可以尝试这样.

  • Copying函数应该确保复制“对象内的所有成员变量”及“所有base class成分”。
  • 不要尝试以某个copying函数实现另一个copying函数。应该将其共同机能放进第三个函数中,并由两个copying函数共同调用。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个基于动态数组的矩阵类的实现,包括类的构造析构、拷贝、赋值等基本操作,以及矩阵的按元素加减乘除运算,矩阵的乘法、转置和求逆等运算: ```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、付费专栏及课程。

余额充值