您的位置:首页 > 编程语言 > C语言/C++

c++的初始化与清除

2016-03-02 19:44 309 查看

《c++编程思想》阅读笔记(4)

第4章 初始化与清除

第2章利用了一些分散的典型c语言库的构件,并把它们封装在一个struct总,从而在库的应用方面做了有意义的改进(从现在起,这个抽象数据类型称为类)

1)这样用类名隐藏了类内部的函数名,并且通过设立处理界限,意味着数据类型的内部机制对设计者来说是可控的和能自行处理的。封装和实现的隐藏大大地改善了库的使用。而安全性包括初始化和清除两个方面。在c语言中,如果一个程序员忘记了初始化或清除一个变量,就会导致一大段程序错误。特别是当用户不知如何对一个struct初始化,甚至不知道必须要初始化时。。。清除是一个特殊的问题,因为c程序员一旦用过一个变量后就把它给忘记了,所以对一个库的struct来说,必要的清除工作往往被遗忘了。在c++中,初始化和清除的概念是简化类库使用的关键所在,并且可以减少那些由于用户忘记这些操作而引起的许多细微错误

2)在c++中,初始化是在太重要了,所以不能把它留给用户来完成。类的设计者可以通过提供一个叫做构造函数的特殊函数来保证每个对象都正确地初始化。如果一个类有构造函数,编译器在创建对象时就自动调用这一函数,这一切在用户是用他们的对象之前就已经完成了。对用户来说,是否调用构造函数并不是可选的,它是由编译器在对象定义时完成的。

3)函数名字必须考虑两点:首先这个名字不能与类的其他成员函数冲突,其次,因为该函数是由编译器调用的,所以编译器必须总能知道调用哪个函数。由此得出:构造函数的名字和类的名字一样。这使得这样的函数在初始化时自动被调用。

4)像其他函数一样,我们也可以通过构造函数传递参数,指定对象该如何创建,设定对象的初始值等等。构造函数的参数保证对象的所有部分都被初始化成合适的值。

5)小结:构造函数是一个有着特殊名字,有编译器自动为每个对象调用的函数然而它解决了类的很多问题,并使得代码更容易阅读,比如在c中我们可能要设一个函数initialize(),这些函数从概念上说是与定义分开了。在c++中,定义和初始化时同一概念,不能只取其中之一。

6)构造函数和析构函数是两个非常特殊的函数:它们没有返回值。这与返回值为void的函数显然不同。后者虽然也不返回任何值,但是我们还可以让它做点别的。而构造函数和析构函数则不允许。在程序中创建和消除一个对象的行为非常特殊,就像出生和死亡,而且总是由编译器来调用这些函数以确保它们被执行。如果它们有返回值,要么编译器必须知道如何处理返回值,要么就只能有用户自己来显示地调用构造函数和析构函数,这样一来,安全性就被破坏了。

7)用析构函数来确保清除

作为一个c程序员,我们可能会经常想到要初始化的重要性,但很少想到清除的重要性。毕竟清除一个整形变量时需要作什么?只需要忘记它。然而,在一个库中,对于一个曾经使用过的对象,仅仅“忘记它”是不安全的。如果我们只是“忘记它”,我们的对象就永远不会消失,在c++中,清除就和初始化一样重要。通过析构函数来保证清除的执行。

8)析构函数的语法和构造函数一样,用类的名字做函数名,再在前面加上一个~,以和构造函数区别。另外,析构函数不带任何参数,因为析构不需要任何选项。当对象超出它的定义范围时,编译器自动调用析构函数。我们知道,在对象的定义点出构造函数被调用,但西沟函数调用的唯一根据是包含该对象的右括号。举个例子来表示:

// 头文件
#ifndef DESTRUCTORS_H
#define DESTRUCTORS_H

class tree {
private:
int height;
public:
tree(int initialHeight);  //constructor
~tree();  //Destructor
void grow(int years);
void printsize();
};

#endif


//实现函数
#include<stdio.h>
#include<destructors.h>

tree::tree(int initialHeight) {
height = initialHeight;
}

tree::~tree() {  // 如果被析构的同时什么都不想做,就直接{}好了
puts("inside tree destructor");
printsize();
}

void tree::grow(int years) {
height += years;
}

void tree::printsize() {
printf("tree height is %d\n", height);
}


// 测试函数
#include<stdio.h>
#include<destructors.h>

int main() {
puts("before opening brace");
{
tree t(12);
puts("after tree creation");
t.printsize();
t.grow(4);
puts("before closing brace");
}
puts("after closing brace");
return 0;
}


运行结果是:

before opening brace

after tree creation

tree height is 12

before closing brace

inside tree destructor

tree height is 16

after closing brace

9)在c中,我们总要在一个程序块的左括号一开始就定义好所有的变量,这在程序设计语言中并不少见(Pascal中除外),其理由无非是“这是一个好的编程风格”,但是作为程序员,如果需要增加一个变量时总得调到块的开始;而如果变量定义紧靠着变量的使用处是,程序的可读性更强。

10)在c++中,如果在一开始就要把所有的对象定义出来,我们不一定会在所有对象开始定义是知道它们的初始化信息。这样很容易将定义与初始化的部分分开,然而c++要保证在一个对象产生时,它同时被初始化。这样可以保证我们的系统中没有为初始化的对象。

通常,在c++中,在还不拥有构造函数的初始化信息是不能创建一个对象,所以不必在块的开头定义所有的变量 这也意味着我们可以等到我们已经知道一个变量的必要信息时再去定义它,所以我们总是可以同时定义和初始化一个变量。而且我们也应该这样去做。

11)因为对象的构造函数与析构函数都是自动调用的,所以类的用户只要把精力集中于怎样使用这些对象上,而不需要担心它们是否已被正确地初始化和清除了

小结:因为构造函数与析构函数让我们保证正确地初始化和清除对象(编译器不允许没有调用构造寒素与析构函数就直接创建与销毁一个对象),是我们得到了完全的控制与安全。

编码期间的安全性是c++中的一大问题,初始化和清除是这其中的一个重要部分,随着本书的进展,我们可以看到其他的安全性问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: