0%

Item 20: Prefer pass-by-reference-to-const to pass-by-value.

缺省情况下C++ 用传值得方式(一个继承自C的方式)传递对象至(或来自)函数。除非你另外指定,否则函数参数都是以实际实参的复件(副本)为初值,而调用端所获得的亦是函数返回值的一个复件。这些复件(副本)系由对象的copy构造函数产出。

尽量以传常量引用替换传值前者通常比较高效,并可避免切割问题 (slicing problem),但是内置类型和 STL 迭代器,还是传值更加合适。

阅读全文 »

什么是 MIME 类型?

MIME(多用途 Internet 邮件扩展)的类型来识别文件格式。 MIME 类型构成了 Internet 上对文件类型进行分类的标准方法。

  • MIME Type是用于描述文件的类型的一种表述方法,其将文件划分为多种类型,方便对其进行统一的管理。
  • MIME Type指定了文件的类型名称、描述、图标信息,同时通过与.desktop应用程序描述文件整合,指定了文件的打开方式。
阅读全文 »

Linuxtr 命令用于转换或删除文件中的字符。

语法

1
$ tr [OPTION] SET1 [SET2]

选项

1
2
3
4
-c, --complerment:反选设定字符。也就是符合 SET1 的部份不做处理,不符合的剩余部份才进行转换;
-d, --delete:删除所有属于第一字符集的字符;
-s, --squeeze-repeats:把连续重复的字符以单独一个字符表示;
-t, --truncate-set1:先删除第一字符集较第二字符集多出的字符;
阅读全文 »

特征 new malloc
类型 关键字 标准库函数
支持语言 C/C++ 只在 C++
申请内存的位置 自由存储区 free store
分配内存的大小 编译器根据类型信息自行计算 显示指定字节数
内存分配失败 抛出 bac_alloc 异常, 不会返回 NULL 返回 NULL
返回类型 返回对象类型的指针 返回void 指针,需要转换
是否调用构造函数/析构函数 调用 不调用
处理数组 new[] 手动指定数组的大小
是否支持重载 支持 不支持
是否支持内存扩充 不支持 realloc
内存释放方式 new/delete, new[]/delete[] malloc/free

Item 17: Store newed objects in smart pointers in standalone statements.

以单独的语句将 new 的对象放入智能指针内。这是为了防止由于其他表达式抛出异常而导致的资源泄漏

举个栗子:

1
processWidget(shared_ptr<Widget>(new Widget), priority());

上述代码中,在 processWidget 函数被调用之前参数会首先得到计算。可以认为包括三部分的过程:

  1. 执行 new Widget
  2. 构造 shared_ptr<Widget>
  3. 调用 priority()

因为C++不同于其他语言,函数参数的计算顺序很大程度上决定于编译器,编译器认为顺序应当是1, 3, 2,即:

  1. 执行 new Widget
  2. 调用 priority()
  3. 构造 shared_ptr<Widget>

那么如果 priority抛出了异常,新的 Widget 便永远地找不回来了。虽然我们使用了智能指针,但资源还是泄漏了!

于是更加健壮的实现中,应当将创建资源和初始化智能指针的语句独立出来:

1
2
shared_ptr<Widget> pw = shared_ptr<Widget>(new Widget);
processWidget(pw, priority());

Item 16: Use the same form in corresponding uses of new and delete.

如果你用 new 申请了动态内存,请用 delete 来销毁;如果你用 new xx[] 申请了动态内存,请用 delete[] 来销毁:

举个栗子:

1
2
3
4
5
std::string* stringPtrl = new std::string;
std::string* stringPtr2 = new std::string[lOO];
...
delete stringptrl; // 删除一个对象
delete [] stringPtr2; // 删除一个由对象组成的数组

上面很容易理解但需要注意typedef:

1
2
typedef std::string AddressLines[4];    //每个人的地址有四行,
//每行是一个string

由于 AddressLines 是个数组,如果这样使用 new:

1
2
std::string *pal = new AddressLines;     //注意. "new AddressLines" 返回
//一个 string*,就像 "new string[4]" 一样

那就必须匹配 “数组形式“的 delete:

1
2
delete pal;         //行为未有定义!
delete [] pal; //很好。

为避免诸如此类的错误,最好尽量不要对数组形式做 typedefs 动作。可以使用更加面向对象的vectorstring等对象。

Item 15: Provide access to raw resources in resource-managing classes.

APIs 往往要求访问原始资源(raw resources),所以每一个RAII class 应该提供提供对原始资源访问的方法。获取资源的方式有两类:隐式地获取和显式地获取。 显式的资源获取会更安全,它最小化了无意中进行类型转换的机会。

  • 显示获取

shared_ptr 提供了 get 方法来得到资源。

1
2
3
4
shared_ptr<Investment> pInv;
void daysHeld(Investment *pi);

int days = daysHeld(pInv.get());

为了让 pInv 表现地更像一个指针,shared_ptr还重载了解引用运算符(dereferencing operatoroperator->operator*

1
2
3
4
5
6
7
8
class Investment{
public:
bool isTaxFree() const;
};
shared_ptr<Investment> pi1(createInvestment());

bool taxable1 = !(pi1->isTaxFree());
bool texable2 = !((*pi1).isTaxFree());

我们封装了Font来管理资源:

1
2
3
4
5
6
7
class Font{
FontHandle f;
public:
explicit Font(FontHandle fh): f(fh){}
~Font(){ releaseFont(f); };
FontHandle get() const { return f; }
};

通过get方法来访问FontHandle:

1
2
3
Font f(getFont());
int newFontSize;
changeFontSize(f.get(), newFontSize);
  • 隐式地获取

可以隐式类型转换运算符将 Font 转换为 FontHandle:

1
2
3
4
5
class Font{
operator FontHandle() const{ return f;}
};

changeFontSize(f, newFontSize);

然而问题也随之出现:

1
FontHandle h2 = f1;

无意间 h2 并没有被资源管理起来,这将会引发意外的资源泄漏。所以隐式转换在提供便利的同时, 也引起了资源泄漏的风险。