题目要求:设计一个电子表格,该电子表格有两个类:Spreadsheet和SpreadsheetCell。每个Spreadsheet都包含了若干个SpreadsheetCell,此外SpreadsheetApplication类管理Spread集合。
step1 : 从最小单元SpreadsheetCell了解类和对象
1 编写类定义和类方法
考虑实际电子表格允许存储的对象可以是数字,可以是文本数据。如果接收文本数据,电子表格将转换为数字。简单的
编程实现如下:
SpreadsheetCell.h 类方法定义
#SpreadsheetCell.h
#ifndef SPREADSHEETCELL_H_
#define SPREADSHEETCELL_H_
#include
class SpreadsheetCell
{
public:
void setValue(double inDouble);
void setString(const std::string& inString);
const std::string& getString() const;
private:
double mValue;
std::string mString;
std::string doubleToString(double inDouble) const;
double stringToDouble(const std::string& inString) const;
};
#endif
SpreadsheetCell.cpp 类方法实现
#SpreadsheetCell.cpp
#include "SpreadsheetCell.h"
#include
#include
using namespace std;
void SpreadsheetCell::setValue(double inDouble){
mString = inDouble;
}
void SpreadsheetCell::setString(const string& inString){
mString = inString;
mValue = stringToDouble(inString);
}
const string& SpreadsheetCell::getString() const{
return mString;
}
string SpreadsheetCell::doubleToString(double inDouble) const{
ostringstream ostr;
ostr << inDouble;
return ostr.str();
}
double SpreadsheetCell::stringToDouble(const string& inString) const{
double dValue;
istringstream istr(inString);
istr >> dValue;
if(istr.fail() || !istr.eof()){
return 0;
}else{
return dValue;
}
}
2 编写对象的创建和使用
在堆栈中的对象调用.method(args):
SpreadsheetCell mCell;
mCell.setValue(5.0);
cout << "mString " << mCell.getString() << endl;
在堆中的对象调用->method(args)
SpreadsheetCell mCell = new SpreadsheetCell();
mCell -> setValue(5.0);
cout << "mStirng " << mCell -> getString() << endl;
delete mCell;
mCell = nullptr;
二者的主要区别:在堆栈中创建的对象无需手动管理内容,创建的对象内存固定;在堆中创建的对象可以动态分配内存,但必须在创建时使用new表达式(首先通过operator new为SpreadsheetCell对象分配内存,然后为这个对象调用构造函数,此处构造函数使用默认构造函数),在销毁时使用delete表达式(首先调用SpreadsheetCell对象的析构函数,然后调用operator delete释放内存)。
在堆中使用对象时,为避免发生内存错误,强烈推荐使用智能指针。
auto mCell = make_unique();
mCell -> setValue(5.0)
cout << "mString " << mCell -> getString() << endl;
3 对象的生命周期:创建、销毁、赋值
1 编写构造函数
//note the var defination order
SpreadsheetCell::SpreadsheetCell(double inDouble):mValue(inDouble), mString(doubleToString(inDouble)){
}
SpreadsheetCell::SpreadsheetCell(double inDouble){
setValue(inDouble);
}
SpreadsheetCell::SpreadsheetCell(const std::string& inString){
setString(inString);
}
在堆栈中调用构造函数:
SpreadsheetCell mCell(5.0);
在堆中调用构造函数:
SpreadsheetCell *mCell = new SpreadsheetCell(5.0);
delete mCell;
mCell = nullptr;
//more efficient way
auto smartmCell = make_unipue(5.0);
注:无论是在堆中还是在类的对象中声明指针,但没有立即初始化对象,都应将指针初始化为nullptr,如果不赋初值nullptr,此时指针未定义,进而可能导致无法预料或难以诊断的内存错误。
2 特殊的构造函数——复制构造函数
SpreadsheetCell::SpreadsheetCell(const SpreadsheetCell& src){
mValue = src.mValue;
mString = src.mString;
}
复制构造函数允许所创建的对象是另一个对象的精确副本。如果没有编写复制构造函数,c++会自动生成一个,用源对象相应数据成员的值初始化新对象的每个数据成员,如果数据成员是对象,意味着调用复制构造函数。
注:c++传递函数参数的默认方式是按值传递,这意味着函数或者方法接收某个值或者对象的副本。因此,无论什么时候给函数或者方法传递一个对象时,编译器都会调用新对象的复制构造函数进行初始化。当函数或者方法返回对象时,也会调用复制构造函数,在此情况下,编译器使用复制构造函数创建一个临时的、没有名称的对象。可将传递的参数作为const引用,从而避免复制构造函数的开销。按引用传递只需要复制对象的地址,不需要复制对象的全部内容,因此比按值传递的效率更高,当按引用传递某个对象时,使用对象引用的函数或方法可以修改原始对象,如果只是为了提高效率才使用按引用传递,可将对象设置为const以排除这种可能。
3 默认析构函数
当销毁对象时,首先调用析构函数,然后释放对象占用的内存。在析构函数中可以执行对象的清理,如释放动态内存或关闭句柄文件。当堆栈中的对象超出作用域时,意味着当前的函数,方法或者代码块结束,对象被销毁。没有智能指针的帮助,在堆中分配的内存不会自动销毁,必须使用delete删除对象指针,从而调用析构函数,并释放内存。
practice makes perfect!