effective c++ 条款05、06(编译器自动生成函数)整理

一、编译器为空类生成函数的原型以及函数创建的时机

在C++中当创建一个空类时,C++就会默认的为这个类创建4个函数:默认的构造函数、析构函数、拷贝构造函数、以及赋值操作符。

C++中创建一个空类:

class Empty {};

默认会生成4个函数,其函数的原型如下:

public:    Empty() { ... }    Empty(const Empty& rhs) { ... }    ~Empty() { ... }    Empty& operator=(const Empty& rhs) { ... }

说明:

1) 这些函数只有在需要调用的时候,编译器才会生成。

2) 4个函数都是public的。

3) 4个函数都是inline的(即函数定义在类的定义中的函数)。

4) 如果你显式的声明了这些函数中的任何一个函数,那么编译器将不再生成默认的函数。

比如,当遇到下列语句时,函数会被编译器生成:

Empty e1;                //默认构造函数                         //对象销毁时,析构函数
Empty e2(e1);            //拷贝构造函数
e2 = e1;                 //赋值运算符

另外,还存在两种默认的函数:就是取地址运算符和取地址运算符的const版本,这两个函数在《Effective C++》中没有提及。

public:    Empty* operator&() { ... }    const Empty* operator&() const { ... }

这两个函数是确实存在的,正如下面的代码可以正常工作:

#include <stdio.h>
class Empty {};
int main(int argc, char** argv)
{
	Empty a;
        const Empty *b = &a;
	printf("%p/n", &a);             //调用取地址运算符
	printf("%p/n", b);              //调用const取地址运算符
}

一个容易被忽略的问题:自定义的拷贝构造函数不仅会覆盖默认的拷贝构造函数,也会覆盖默认的构造函数。下面的代码是编译不过的,用户必须再显式的定义一个无参的构造函数。

class Empty
{
public:
        Empty(const Empty& e) { }    //拷贝构造函数
};
int main(int argc, char** argv)
{
    Empty a;
}

二、自动生成的赋值操作符的问题

赋值操作符函数的行为与拷贝构造函数的行为基本是相同的,编译器生成赋值操作符函数是有条件的,如果会产生无法完成的操作,编译器将拒绝产生这一函数。那么什么时候编译器无法完成赋值这一行为呢?考虑如下情形(来源Effective C++):

template<class T>
class NameObject
{
public:
    NameObject(std::string& name, const T& value);
private:
    std::string& nameValue;    //引用成员变量
    const T objectValue;       //const成员变量
};
// 然后考虑下面的语句会发生什么事:
std::string newDog("abc");
std::string oldDog("xxx");
NameObject<int> p(newDog, 2);
NameObject<int> s(oldDog, 10);
p = s; //将会发生什么?

赋值语句之前,p.nameValue指向newDog, s.nameValue指向oldDog。那么赋值之后呢?p.nameValue应该指向s.nameValue指向的对象吗?

但是C++有一条规定:引用不能改指向另外一个对象。

对于变量objectValue,C++规定:更改const成员是不合法的。

因此如果上面两种情形中的任何一种发生了,C++编译器给出的响应是:拒绝编译这一行的赋值动作。如果你这么做了,C++编译器会报错。

如果你执意要进行赋值操作,那么可以自己定义一个赋值操作符重载函数。

三、隐藏编译器自动生成的函数

为了驳回编译器自动(暗自)提供的机能,可将相应的成员函数声明为private并且不予实现,如下

class HomeForSale
{
  public:
      ...
  private:
     HomeForSale(const HomeForSale&);
     HomeForSale& operate=(const HomeForSale&);  //此时都无需写参数名.因为不需要提供实现
};

由于编译器产出的拷贝构造函数和赋值函数都是public,为阻止这些函数被创建出来,我们需要自行声明它们,但这里我们将它们声明为private,由此明确阻止了编译器暗自创建专属版本,而令这些函数为private,使得可以成功阻止别人调用它

当然,函数成员和友元还是可以调用的,只是没有实现这些函数,这个方法简单易用,书中的另外一种方法多重继承下很容易引起混乱,不看好。

四、C++11新变化

C++11中引入了“右值引用”和“移动语义”的概念,可以实现对右值的引用。(左值和右值的解释可以见http://amyz.itpub.net/post/34151/411832)

移动语义,简单来说,就是在一个右值对象的生命期结束之前,将其资源偷过来,为我所用。有关移动语义的详细内容这里不做详述,大家可以参见csdn上一篇文章 http://blog.csdn.net/pongba/article/details/1684519。这里要说明的是移动构造函数和移动赋值运算符。

1. 移动构造函数和移动赋值运算符重载函数不会隐式声明,必须自己定义。

2. 如果用户自定义了拷贝构造函数或者移动构造函数,那么默认的构造函数将不会隐式定义,如果用户需要,也需要显式的定义。

3. 移动构造函数不会覆盖隐式的拷贝构造函数。

4. 移动赋值运算符重载函数不会覆盖隐式的赋值运算符重载函数。

时间: 2024-10-05 16:42:46

effective c++ 条款05、06(编译器自动生成函数)整理的相关文章

Effective C++ .06 阻止编译器自动生成函数以及被他人调用

这节讲了下如何防止对象拷贝(隐藏并不能被其他人调用) 两种方法: 1. 将拷贝构造函数声明为private 并且声明函数但不进行定义 #include <iostream> #include <cstdlib> class Dummy { public: Dummy(int d = 0) : data(d) {} Dummy* getCopy() { Dummy* x = new Dummy(*this); return x; } private: int data; Dummy(

effective c++ 条款 04 (对象初始化)整理

确定对象使用前已被初始化 原则:不论是类的成员变量还是其他作用域的变量,使用前都要保证已被初始化(或者说赋值) 一.无任何成员的内置类型初始化 /*内置类型通过复制完成初始化*/ int x = 0; const char* a = "abc"; int a[2] = {0,0}; 二.STL容器初始化 STL容器关注容器大小,防止越界,初始化的工作不用关心 三.类成员变量初始化 参考:http://www.cnblogs.com/BlueTzar/articles/1223169.h

Effective C++学习笔记 条款06:如不想使用编译器自动生成的函数,就该明确拒绝

一.为驳回编译器自动提供的机能,可将相应成员函数声明为private并且不予实现.(如果你仅仅是自己不实现的话,编译器会帮你实现) 如: class A { public: A(const string& name):m_name(name) {} private: //拒绝copy和赋值,声明为private,并且只声明不实现 A(const A&); A& operator=(const A&); private: string m_name; }; 二.对于拒绝赋值的

effective c++ 条款06:若不想使用编译器自动生成的函数,就该明确拒绝

记住:为防止编译器暗自提供的功能,可将相应的成员函数声明为privae并且不予实现.也可以使用Uncopyable这样的父类实现. 对于独一无二的对象,希望不支持拷贝构造函数和赋值操作符. class HomeForSale { public: ... private: HomeForSale(const HomeForSale&); //只是声明,阻止编译器自动生成 HomeForSale& operator=(const HomeForSale&); } HomeForSale

【Effective c++】条款6:若不想使用编译器自动生成的函数就应该明确拒绝

地产中介卖的是房子,其使用的中介软件系统应该有个类用来描述卖掉的房子 class HomeFoeSale { ......} 但是任何房子都是独一无二的,不应该存在两个房子拥有同样的属性,因此以下操作不应该正确! HomeForSale h; HomeForSale h1(h); //调用复制构造函数 HomeForSale h2 = h; //调用赋值操作符 阻止这两个操作(复制.赋值)可以不声明它们,but自己不声明,编译器会自动生成,并且访问权限还是public.没办法只好声明出来,但是如

Effective C++ 条款六 若不想使用编译器自动生成的函数,就该明确拒绝

class HomeForSale //防止别人拷贝方法一:将相应的成员函数声明为private并且不予实现 { public: private: HomeForSale(const HomeForSale&); HomeForSale& operator = (const HomeForSale&);//只有申明,此函数很少被使用   };   //方法二,设计一个专门用来阻止copying动作的基类,然后让其他类继承这个类即可   class Uncopyable { prot

Effective C++ (笔记) : 条款05 -- 条款10

条款05:了解C++默默编写并调用哪些函数 编译器可以暗自为class创建default构造函数.copy构造函数.copy assignment操作符,以及析构函数. 只有这些函数需要(被调用)时,它们才会被编译器创建出来.在编译器产生的复制构造函数和赋值运算符执行的都是浅拷贝.当数据成员是引用或者常量的时候,编译器不知道该怎么处理,两手一摊,无能为力. 当某个基类将copy assignment操作符声明为私有的,编译器将拒绝为派生类生成copy assignment操作符,因为它无权(也不

Effective C++ 之 Item 6 : 若不想使用编译器自动生成的函数,就该明确拒绝

Effective C++ chapter 2. 构造 / 析构 / 赋值运算 (Constructors, Destructors, and Assignment Operators) Item 6. 若不想使用编译器自动生成的函数,就该明确拒绝 (Explicitly disallow the use of compiler-generated functions you do not want) 地产中介商卖的是房子,一个中介软件系统自然而然想必有个 class 用来描述待售房屋: cla

Effective C++ Item 6 若不想使用编译器自动生成的函数,就该明确拒绝

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 为驳回编译器自动提供的机能,可将相应的成员函数声明为private并且不予实现.使用像Uncopyable这样的base class也是一种方法 classUncopyable{ protected: //允许derived对象构造和析构 Uncopyable(){} ~Uncopyable(){} private: Uncopyable(constUncopyable&); //但阻止c