C++空类中的默认函数
定义一个空的C++类,例如
class Empty
{
}
一个空的class在C++编译器处理过后就不再为空,编译器会自动地为我们声明一些member function,一般编译过去就相当于
class Empty
{
public:
Empty(); // 缺省构造函数
Empty( const Empty& ); // 拷贝构造函数
~Empty(); // 析构函数
Empty& operator=( const Empty& ); // 赋值运算符
Empty* operator&(); // 取址运算符
const Empty* operator&() const; // 取址运算符 const
};
一般的书上好像都是前面四种:默认构造函数,拷贝构造函数,默认赋值函数以及析构函数,后面两种其实属于,但要需要注意的是,只有当你需要用到这些函数的时候,编译器才会去定义它们。
这些函数只有在第一次被调用时,才会别编译器创建。所有这些函数都是inline和public的。
默认的析构函数是非虚函数(除非基类有自己声明的虚析构函数)。
而拷贝默认构造函数和默认拷贝赋值操作符知识是单纯将来源对象的每一个非静态成员拷贝到对象目标中(bitwise copy)。
其中的默认拷贝赋值操作符只有在生成的代码合法并且有机会证明它有意义存在时才会生成。这就说明,
如果你打算在一个“内含引用成员”或者“内含const成员”的类内支持赋值操作,就必须定义自己的默认拷贝赋值操作符。因为C++本身不允许引用改指不同的对象,也不允许更改const成员。
最后一种情况,
当基类将自己的默认拷贝赋值操作符声明为private时,子类就不会产生自己的的默认拷贝赋值操作符。因为假如产生了这样的默认拷贝赋值操作符,它会试着去调用基类的默认拷贝赋值操作符去处理基类的部分,不幸的是,它没有权利。
你可以将拷贝构造函数或默认拷贝赋值操作符声明为private。这样明确声明一个成员函数,就阻止了编译器暗自创建的默认版本,而这些函数为private,使得可以成功阻止人们调用它。
上面的做法有一个隐患,因为类自身的member和friend还是可以调用这些private函数。有一个很刁钻的方法,“
将成员函数声明为private而且故意不实现它们”,这样既阻止了默认函数的生成,而且如果你试着调用这些函数,就会得到一个链接错误。只声明,不定义,链接器报错。甚至在声明的时候,你连参数也不用写。
而试着将上述的链接器错误提前到编译器也是可以的。我们专门设计一个类Unconpyable。
--------------------------------------------------------------------
class Uncopybale {
protected:
Uncopyable() {}
~Uncopyable() {}
private:
Ucopyable(const Uncopyable&)
Uncopyable& operator=(const Uncopyable&)
};
--------------------------------------------------------------------
为了阻止对象被拷贝,
我们唯一需要做的就是继承Uncopyable。这些函数的默认生成版本会尝试调用其基类的对应版本,那些调用会被编译器拒绝,因为它基类的拷贝函数是private。
Boost提供的noncopyable类也有类似的功能。
忠告:
为了驳回编译器自动提供的技能,可将相应的成员函数声明为private并且不予实现。使用像Uncopyable这样的基类也是一种做法。
并不是所有的编译器都包成对象的内置类型成员会被自动初始化为0。永远在使用对象之前先将它初始化。确保每一个构造函数都将对象的每一个成员初始化。
别把赋值错当成初始化。C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前(对于内置类型对象可能不确定),这点对于非内置类型对象来说尤其关键。如果你没有在成员初始化列表(member initialization list)为其初始化,它们将调用自己的默认构造函数,然后才进入构造函数内部(很可能你会在这里给他们赋值)。在成员初始化列表中的初始化只是调用了拷贝构造函数一次,而在构造函数内部再为其赋值则在调用默认构造函数后又调用了一次拷贝构造函数。哪个效率高你当然知道。 所以,请用成员初始化列表进行初始化,虽然效率提高只针对于非内置类型成员,但是规定总是在初值列中雷楚所有成员变量,这样就省的有些未被列出的内置类型成员被忘记初始化。而有些时候,即使成员变量是内置类型,也必须要用成员初始化列表(成员变量为const或者reference,它们一定要有初值,而且不能被赋值)。 总之,总是使用成员初始化列表,这样或者必要,或者高效。有个例外,当你重载多个构造函数,每个构造函数有很多成员变量和基类的时候(这意味这成员初始化列表会很多、很长而且重复较多),可以将一些内置类型变量的初始化动作(它们的赋值和初始化不影响效率)移到一个私有函数中,供所有的构造函数调用。 规定:初始化顺序是基类早于派生类,类成员变量则以其声明顺序为准。所以成员初始化列表中列出的各个成员的顺序最好与声明的顺序相同。 最后说个不常见的问题:某个对象A的非静态成员变量初始化动作正好使用了另外一个编译单元(另外一个cpp)中的某个非静态对象B,你不能保证A在需要B的时候,B就已经被编译好而且产生了。解决的办法是将对象A和对象B都分别放到函数中(貌似是专门为每个这样的对象定制的对象),并且声明为static。这些函数返回的是静态对象的引用。这是单例模式的一种实现。在程序中以前需要对象引用的地方直接调用这些函数就好了。这种reference-returning函数对于处理多线程环境下的“竞速形势(race conditions”的方法是:在程序的单线程启动阶段手工调用所有的reference-returning函数。 忠告: 1 为内置对象进行手工初始化,因为C++不保证初始化它们。 2 构造函数最好使用成员初始化列表(member initialization list),而不要在构造函数本体内使用赋值操作。初始化列表列出的成员变量,其排列次序应该和它们在class中的声明次序相同。 3 为免除“跨编译单元的初始化次序”问题,请以local static对象替换non-local static对象。 |
C++空类中的默认函数的更多相关文章
- C++中的空类,编译器默认可以产生哪些成员函数
C++中的空类,编译器默认可以产生哪些成员函数 C++中创建一个空类:class Empty {};默认会生成4个函数,其函数的原型如下: public: Empty() { ... } Empty( ...
- C++:类中的赋值函数
先来看一个例子: #include<iostream> #include<string> using namespace std; class Student{ public: ...
- C++中若类中没有默认构造函数,如何使用对象数组
前言: 如果定义一个类,有其默认的构造函数,则使用new动态实例化一个对象数组,不是件难事,如下代码: #include <memory> #include <iostream> ...
- C++(十六) — 类中引用成员函数、命名空间的使用
1.为什么类中引用成员函数? 类将属性和方法做了封装.类是一种数据类型,也就是:固定大小内存块的别名. 类的定义是一个抽象的概念,定义时不分配内存,当用类定义对象时,才分配一个固定大小的内存块. 此时 ...
- c++类大四个默认函数-构造函数 析构函数 拷贝构造函数 赋值构造函数
每个类只有一个析构函数和一个赋值函数,但可以有多个构造函数(包含一个拷贝构造函数,其它的称为普通构造函数).对于任意一个类A,如果不编写上述函数,C++编译器将自动为A 产生四个缺省的函数,例如: A ...
- C++空类产生哪些成员函数 || C++类可以自动生成的6个成员函数
class Empty { public: Empty(); // 缺省构造函数 Empty( const Empty& ); // 拷贝构造函数 ~Empty ...
- C++(1)C++类四个默认函数---构造函数、析构函数、拷贝函数、赋值函数
C++构造函数和析构函数 默认构造函数指不带参数或者所有参数都有缺省值的构造函数!!! (1)构造函数.析构函数与赋值函数 构造函数.析构函数与赋值函数是每个类最基本的函数.它们太普通以致让人容易麻痹 ...
- C++类四个默认函数&深复制&浅复制
学习C++语言的同学都知道,C++中类是有默认的几个函数的,主要是有四个函数: 四个函数 默认构造函数:A(void),无参构造函数 拷贝(复制)构造函数:A(const A&a).用一个对象 ...
- 向OC类中添加默认的协议实现(ProtocolKit)
以forkingdog的PorotocolKit举例 举例 ProtocolKit Protocol extension for Objective-C Usage Your protocol: @p ...
随机推荐
- 浅谈管道模型(Pipeline)
本篇和大家谈谈一种通用的设计与处理模型--Pipeline(管道). Pipeline简单介绍 Pipeline模型最早被使用在Unix操作系统中.据称,假设说Unix是计算机文明中最伟大的发明,那么 ...
- ASP.NET中过滤HTML字符串的两个方法
先记下来,以作备用! /// <summary>去除HTML标记 /// /// </summary> /// <param name="Htmlstring& ...
- PowerDesigner与UML建模应用
一. PD简介 PowerDesigner 是一个集所有现代建模技术于一身的完整工具,它集成了强有力的业务建模技术.传统的数据库分析和实现,以及UML对象建模.通过了元数据的管理.冲突分析和真正的 ...
- SilverLight 页面后台方法XX.xaml.cs 创建JS,调用JS ,弹出提示框
1.Invoke和InvokeSelf [c-sharp] view plaincopy public partial class CreateJSDemo : UserControl { publi ...
- 关于Oracle dmp文件导入随笔
进入博客园已经两年多了,每次想写点什么,都是给自己个各种借口,不了了之~今天就从Oracle数据库最长用的导入开始吧! 1.低版本的exp/imp可以连接到高版本(或同版本)的数据库服务器,比如:10 ...
- C#在foreach循环中修改字典等集合出错的处理
C#在foreach循环中修改字典等集合出错:System.InvalidOperationException: Collection was modified; enumeration operat ...
- 0129——UINavigationController
1.创建一个UINavigationController self.window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].boun ...
- web 安全 初探 (正在更新)
1.web应用程序所采用的防卫机制的几个核心构成:1.处理用户对应用程序的数据和功能的访问,以防止用户未经授权访问.2.处理用户的输入,以防止恶意的输入导致未预期的行为.3.处理攻击,以确保应用程序在 ...
- C#按键打开文件选择对话框,并把选择好的路径保存/显示到textBox
1.选择文件夹 FolderBrowserDialog fbd = new FolderBrowserDialog(); fbd.SelectedPath = "D:";//默认路 ...
- shell编程-1到100的求和与冒泡排序
Shell编程 一. for循环 生成列表 {起始数..结束数} 命令生成列表 `seq [起始数] [步进长度] 结束数 ` for l in {1..5};do for l in `seq ...