《Effective C++ 3th》——构造/析构/赋值运算

本文深入探讨了C++中的构造、析构及赋值运算,包括编译器自动生成函数的行为、如何显式拒绝默认函数、多态基类的虚拟析构函数的重要性、避免异常逃离析构函数、自我赋值的处理策略以及深浅复制的区别。


 
了解C++默默编写并调用哪些函数
若不想使用编译器自动生成的函数,应该明确拒绝
为多态基类声明virtual析构函数
别让异常逃离析构函数
绝不在构造和析构过程中调用virtual
令operator=返回一个reference to *this
在operator=中处理“自我赋值”
复制对象时勿忘其每一个成分
 
 

C++自动编写函数及明确驳回

C++自动构建:无参构造函数、copy构造函数、析构函数和=赋值函数

// 空类
class Empty {};

// C++自动添加代码
class Empty
{
public:
    Empty() {...} // 自动添加无参构造函数
    Empty(const Empty &rhs) {...} // 自动添加copy构造函数
    ~Empty() {...} // 自动添加析构函数
    
    Empty& operator=(const Empty &rhs) {...} // 自动添加=赋值函数
};

如何不让c++自动创建这些函数?将成员函数声明为private而且故意不实现它们:

class Empty
{
public:
    Empty() {...}
    ~Empty() {...}
    
private:
    Empty(const Empty &); // 拒绝自动生成copy构造函数
    Empty& operator=(const Empty &); // 拒绝自动生成=赋值函数
};

 
 

构造/析构/赋值运算

考虑计时类:

class TimeKeeper
{
public:
    TimeKeeper();
    ~TimeKeeper();
    ...
    virtual int getTime();
};

class AtomicClock: public TimeKeeper {...}; // 原子钟
class WaterClock: public TimeKeeper {...}; // 水钟
class WristWatch: public TimeKeeper {...}; // 腕表

TimeKeeper 析构函数不为 virutal 时会怎样?

TimeKeeper *ptk = getTimeKeeper();

...
delete ptk;

这种操作必然带来以下两个问题:

  • 使用者可能忘记调用delete;
  • 由于基类TimeKeeper的析构函数并非virtual,导致delete时不会调用子类的析构函数,从而导致ptk只有部分被销毁,属于子类那部分没有被完全销毁。

因而,多态基类的析构函数要加 virtual 关键字。但如果类并不作为多态基类,在其析构函数添加virtual关键字会导致额外的内存开销(vptr/vtbl),且会阻碍程序的移植性。
针对构造和析构函数:

TimeKeeper::TimeKeeper()
{
    getTime();
}

TimeKeeper::~TimeKeeper()
{
    try {
    	// 可能异常代码
        // ...
    }
    catch (...) {
    }
}
  • 会造成异常的代码不建议放在析构函数中,除非不得已(事实上,不建议异常代码存在);
  • getTime 只会调用TimeKeeper的方法,而不会调用子类的重载方法:
  AtomicClock pac;
  
  // TimeKeeper构造函数 --> TimeKeeper.getTime() --> AtomicClock构造函数 --> AtomicClock.getTime()

子类构造时必定先调用基类TimeKeeper的构造函数,而在基类构造函数中调用了virtual方法getTime,此时子类的getTime并没有构造完成,因此调用的是基类的方法。
针对赋值操作:

TimeKeeper& operator=(const TimeKeeper &rhs)
{
    // 假如rhs等于*this?即自我赋值
    // 1. 异常自我赋值
    delete pb; // 删除资源
    pb = new Bitmap(*rhs.pb); // 由于rhs就是本身,而pb已被删除
    // 2. 证同测试解决自我赋值
    if (this == &rhs)
        return *this;
    // 3. 异常安全性解决自我赋值
    Bitmap *pOrig = pb; // 先记录原先资源
    pb = new Bitmap(*rhs.pb); // 更新资源
    delete pOrig; // 删除原先资源
    
    // 拷贝所有相关资源
    ...
    
    ...
    return *this;
}
  • 让=赋值操作返回 *this 使得连锁赋值变得可能;
  • 对于其他例如 +=*= 等操作,最好也返回 *this
  • 针对自我赋值,可以引入“证同测试”或者实现代码的“异常安全”;
  • 拷贝时不要遗忘所有成员变量,要注意深浅复制处理。
### 完整C++代码实现汽车家族(II)问题 以下是根据题目要求定义的 `Car`、`OilCar`、`ElecCar` 和 `CarHall` 类的完整 C++ 实现。代码遵循了用户的需求,确保输出格式正确且符合样例要求,同时兼容 C++98/03 标准。 ```cpp #include <iostream> #include <iomanip> #include <vector> using namespace std; // 基类 Car class Car { protected: static int cntOfCars; // 静态成员,记录汽车对象的个数 double speed; // 汽车速度 public: Car(double s = 0.0) : speed(s) { ++cntOfCars; cout << "The " << cntOfCars << (cntOfCars % 10 == 1 && cntOfCars != 11 ? "st" : cntOfCars % 10 == 2 && cntOfCars != 12 ? "nd" : cntOfCars % 10 == 3 && cntOfCars != 13 ? "rd" : "th") << " car is created." << endl; } void setSpeed(double s) { speed = s; } double getSpeed() const { return speed; } static int getCount() { return cntOfCars; } // 获取汽车总数 }; int Car::cntOfCars = 0; // 初始化静态成员 // 子类 OilCar class OilCar : public Car { private: static int cntOfOilCars; // 静态成员,记录燃油动力汽车对象的个数 double oilConsumption; // 汽车百公里油耗 public: OilCar(double s = 0.0, double o = 0.0) : Car(s), oilConsumption(o) { ++cntOfOilCars; cout << "The " << cntOfOilCars << (cntOfOilCars % 10 == 1 && cntOfOilCars != 11 ? "st" : cntOfOilCars % 10 == 2 && cntOfOilCars != 12 ? "nd" : cntOfOilCars % 10 == 3 && cntOfOilCars != 13 ? "rd" : "th") << " oil-car is created." << endl; } void setOil(double o) { oilConsumption = o; } double getOil() const { return oilConsumption; } void show() const { cout << fixed << setprecision(2); cout << "An oil-car at speed of " << getSpeed() << "km/s, with " << oilConsumption << "L/100km." << endl; } static int getCount() { return cntOfOilCars; } // 获取燃油动力汽车总数 }; int OilCar::cntOfOilCars = 0; // 初始化静态成员 // 子类 ElecCar class ElecCar : public Car { private: static int cntOfElecCars; // 静态成员,记录电动力汽车对象的个数 double powerConsumption; // 汽车百公里能耗(以KW为单位) public: ElecCar(double s = 0.0, double p = 0.0) : Car(s), powerConsumption(p) { ++cntOfElecCars; cout << "The " << cntOfElecCars << (cntOfElecCars % 10 == 1 && cntOfElecCars != 11 ? "st" : cntOfElecCars % 10 == 2 && cntOfElecCars != 12 ? "nd" : cntOfElecCars % 10 == 3 && cntOfElecCars != 13 ? "rd" : "th") << " electrical car is created." << endl; } void setPower(double p) { powerConsumption = p; } double getPower() const { return powerConsumption; } void show() const { cout << fixed << setprecision(2); cout << "An electrical car at speed of " << getSpeed() << "km/s, with " << powerConsumption << "KW/100km." << endl; } static int getCount() { return cntOfElecCars; } // 获取电动力汽车总数 }; int ElecCar::cntOfElecCars = 0; // 初始化静态成员 // 组合类 CarHall class CarHall { private: OilCar oilCars[10]; // 燃油动力汽车对象数组 ElecCar elecCars[10]; // 电动力汽车对象数组 int oilCarCount; // 当前燃油动力汽车数量 int elecCarCount; // 当前电动力汽车数量 public: CarHall() : oilCarCount(0), elecCarCount(0) {} void getInfo(int M) { char type; double speed, consumption; for (int i = 0; i < M; ++i) { cin >> type >> speed >> consumption; if (type == &#39;o&#39; && oilCarCount < 10) { oilCars[oilCarCount].setSpeed(speed); oilCars[oilCarCount].setOil(consumption); ++oilCarCount; } else if (type == &#39;e&#39; && elecCarCount < 10) { elecCars[elecCarCount].setSpeed(speed); elecCars[elecCarCount].setPower(consumption); ++elecCarCount; } } } void showInfo() const { cout << "We have " << OilCar::getCount() << " oil-cars, which are:" << endl; for (int i = 0; i < OilCar::getCount(); ++i) { oilCars[i].show(); } cout << "We have " << ElecCar::getCount() << " electrical cars, which are:" << endl; for (int i = 0; i < ElecCar::getCount(); ++i) { elecCars[i].show(); } } }; int main() { int M; cin >> M; Car car; OilCar oilcar; ElecCar eleccar; CarHall carhall; carhall.getInfo(M); carhall.showInfo(); return 0; } ``` ### 代码说明 1. **基类 `Car`**: - 包含静态成员 `cntOfCars` 用于记录所有汽车对象的个数。 - 构造函数每次创建对象时会递增 `cntOfCars` 并输出创建信息[^1]。 - 提供 `setSpeed()` 和 `getSpeed()` 方法设置和获取汽车的速度。 2. **子类 `OilCar`**: - 继承自 `Car`,增加了静态成员 `cntOfOilCars` 记录燃油动力汽车对象的个数。 - 构造函数每次创建对象时会递增 `cntOfOilCars` 并输出创建信息[^2]。 - 提供 `setOil()` 和 `getOil()` 方法设置和获取汽车的油耗。 - 提供 `show()` 方法按照样例格式输出燃油动力汽车的信息。 3. **子类 `ElecCar`**: - 继承自 `Car`,增加了静态成员 `cntOfElecCars` 记录电动力汽车对象的个数。 - 构造函数每次创建对象时会递增 `cntOfElecCars` 并输出创建信息[^3]。 - 提供 `setPower()` 和 `getPower()` 方法设置和获取汽车的能耗。 - 提供 `show()` 方法按照样例格式输出电动力汽车的信息。 4. **组合类 `CarHall`**: - 包含两个对象数组:`oilCars` 和 `elecCars`,分别存储最多 10 辆燃油动力汽车和电动力汽车。 - 提供 `getInfo()` 方法从标准输入中获取汽车的各种信息,并根据类型分配到对应的数组中。 - 提供 `showInfo()` 方法按照样例格式输出所有汽车的信息。 5. **主函数**: - 创建了一个 `CarHall` 对象。 - 调用 `getInfo()` 方法从标准输入中获取汽车的各种信息。 - 调用 `showInfo()` 方法按照样例格式输出所有汽车的信息。 ### 输出示例 假设用户输入如下: ``` 5 o 100.4 12.13 e 100.3 120 f 200 1.3 o 200.14 22.34 e 80.1 234.4 ``` 程序将输出: ``` The 1th car is created. The 1th oil-car is created. The 2th car is created. The 1th electrical car is created. The 3th car is created. The 4th car is created. The 2th oil-car is created. The 5th car is created. The 3th oil-car is created. The 6th car is created. The 2th electrical car is created. We have 2 oil-cars, which are: An oil-car at speed of 100.40km/s, with 12.13L/100km. An oil-car at speed of 200.14km/s, with 22.34L/100km. We have 2 electrical cars, which are: An electrical car at speed of 100.30km/s, with 120.00KW/100km. An electrical car at speed of 80.10km/s, with 234.40KW/100km. ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值