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

几种特殊性质的类--《Effective C++》《More Effective C++》笔记

2015-02-03 20:17 441 查看

0 C++的灵活性

C++语言实在是太灵活了,所以《Effective C++》提议把它看做四门语言的集合。其复杂的语法可以让程序员实现任意意图,同时由于过于复杂而导致正确表达的难度很大。本文总结实用的一些表达方式。

1 对象的创建、初始化、删除方式

C++对象创建方式有两种:在stack或data区上自动创建;
在Heap上手动创建。
当直接声明一个对象时,就采用第一种自动创建的方式,编译器为其自动开辟内存;当使用new来创建对象时就对应于第二种,即在heap上创建对象。
C++对象创建(开辟内存)之后并不能直接使用,而是在使用之前必须初始化,而这是编译器自动为你添加了初始化调用代码。C++支持三种不同的对象初始化方式。通过构造函数Constructor初始化;
通过拷贝构造函数Copy Constructor初始化;
通过赋值运算符Assignment operator初始化。
这三种是各自独立的初始化方式。任何C++对象都必须调用至少其中一个来完成对象的初始化。
使用完毕后,C++对象需要清理并释放内存,对应于两种创建方式,C++对象有两种删除方式。在stack或data的对象由编译器负责自动删除;
在Heap上的对象必须显式调用delete删除。
特别对于stack来说,一旦C++对象离开作用域(对象名所在的{}),C++编译器自动在此加入删除代码。

2 区分 new operator和operator new

其实很好理解。当在程序中使用new来产生对象时,这个new就是new operator,是C++的关键字,不能重载或修改。new operator的内部大概流程如下:

(1)确定类对象的大小,调用类的一个特殊成员函数来分配所需内存。这个成员函数就是 operator new(size_t size);如果类没有提供,C++编译器默认提供一个全局的::operator new(size_t size)。最简单的实现就是直接调用malloc(size)了。如果分配失败,调用失败处理函数。
(2)初始化对象的成员变量;(3)调用类的构造函数;(4)返回对象地址指针。
可见程序中简单的一个new关键的后面,C++编译器为我们背地里生成了很多汇编代码。至于delete operator和operator delete的关系与new非常类似,不再累述。

3 几个特殊性质的类

了解了前面的知识,下面来看看如何实现几个特殊性质的类。

3.1 无法被继承的类

这在概念上与C#的密封类相似。实现原理为:derived class的构造函数必须调用父类的一个构造函数,子类的析构函数必须调用父类的析构函数,这样只要把构造函数或析构函数设置为private,则derived class的构造函数就无法调用它,而C++编译器又必须生成调用的代码,从而造成矛盾,导致编译失败。代码如下:
// 不能被继承的类(构造函数为private)
class CannotBeInherited
{
    private:
        CannotBeInherited();
};
class D: public CannotBeInherited
{
    public:
        D(){};
};

需要注意的是,如果类D中不手动添加构造函数,编译会成功。这是因为C++编译器采用Lazy 编译模式,知道调用的时刻才去编译生成D的构造函数。无论如何,一旦遇到需要产生D对象的代码,就会导致编译失败。

3.2 只能通过new创建对象的类

这对于C#就是必然的,C#的对象只能通过new来产生。所有对象都在heap上产生能简化很多事情,想想C++对象作为参数和返回值,赋值等种种复杂的处理吧,都是由于heap在stack上使用对象的恶果!那么如何使得一个类的对象只能通过new来创建呢?原理是:C++对于在stack上创建的对象时,必然要从类的外部自动调用其某个构造函数和析构函数。此时只有把其中之一设置为private或者protected,就能阻止外部对其调用,从而导致C++编译失败。
// 不能在Stack上创建对象的类(析构函数为protected或private)
class CannotBeInstantiatedInStack
{
    public:
        Delete()
        {
            delete this;
        }
    protected:
        ~CannotBeInstantiatedInStack();
};
注意,此时需要提供一个public的Delete函数来负责删除对象,否则new出来的对象永远也无法删除了。

3.3 不能通过new创建对象的类

《Effective C++》中提到的一个非常重要的知识点就是:使用对象管理资源,利用stack上对象离开作用域时总是能(无论是否有异常或提前return)自动调用其析构函数的保障来在其析构函数里完成资源清理。这样的对象当然必须在stack上建立。实现原理:new操作符必须在类的外部调用operator new,所以只有让operator new为protected或private就能够阻止C++编译器生成调用它的代码,从而导致编译失败。
// 不能在Heap上创建对象的类(operator new 为 private)
class CannotBeInstantiatedInHeap
{
    private:
        static void* operator new(size_t size);
        static void* operator new[](size_t size);
        static void operator delete(void* ptr);
        static void operator delete[](void* ptr);
};

4 一点思考

C++可以算是最复杂的编程语言了,同时也是最灵活的。可是人的精力是有限的,所以很少有程序员敢说自己精通C++语言。语法太过灵活,从而导致开发模式过多,同行之间交流也有一定困难。随着RTTI和Exception的标准化,C++在迈向成熟现代语言的同时,丢掉了C语言的高效率和简单性,所以在系统编程领域原来越不受欢迎;而在Web等领域,更简单专用的Java,PHP,C#早已经把C++打败;C++目前还生存在图形处理等为数不多的几个领域。

无论如何,C++完成了交给它的历史使命,现存的大量代码也保障了这门语言将长久活在世上。另外,但从学习知识来讲,学习C++这门语言特别是编译器的处理以及其内存模型,对于学习任何其他语言都有强烈的支撑作用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: