C++编译器为类自动生成的函数

我们可以构建一个空类,

class Empty
{
};

尽管没有定义任何函数,但我们可以通过以下方式使用这个类:

Empty e1;
Empty e2(e1);
e2 = e1;

因为当编译器发现你用上述方式使用这个类而却在类声明中没有定义一般构造函数(非复制构造函数)、复制构造函数、赋值操作符重载函数和析构函数时,会自动为其生成这些函数,上面的Empty类经编译后就对应着下面的类:

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

编译器生成的默认构造函数和析构函数的主要任务是分别调用基类和非静态成员变量的构造和析构函数。生成的复制构造函数和赋值操作符重载函数只是单纯的将源对象的每一个非静态变量拷贝给目标对象。

/******************************************
 * simple_class.cpp                       *
 *                                        *
 * C++自动生成的函数                      *
 ******************************************/

#include 
#include 

class Simple
{
  std::string name;
  int age;

public:
  void SetName(std::string name)
  {
    this->name = name;
  }

  void  SetAge(int age)
  {
    this->age = age;
  }
  void Print()
  {
    std::cout<<"name="<","<<"age="<std::endl;
  }
};

int main()
{
  Simple s1;
  std::cout<<"s1: ";
  s1.Print();

  std::cout<<"s1: ";
  s1.SetName("Jim");
  s1.SetAge(20);
  s1.Print();

  std::cout<<"s2: ";
  Simple s2(s1);
  s2.Print();

  Simple s3;
  s3.SetName("Tom");
  s3.SetAge(25);
  std::cout<<"s3: ";
  s3.Print();
  s3 = s2;
  std::cout<<"s3: ";
  s3.Print();

  return 0;
}

生成的函数

赋值操作符重载函数只有当编译器生成的代码是合法且有意义时才会由编译器自动生成。

/****************************************
 * wired_employee.cpp                   *
 *                                      *
 * 不合法的赋值运算符重载函数不会自动生 *
 * 成                                   *
 ****************************************/
#include 
#include 

class WiredEmployee
{
private:
  std::string& name;
  const int age;

public:
  WiredEmployee(std::string& _name, const int& _age) :
    name(_name),age(_age)
  {
  }

  void Print()
  {
    std::cout<<"name="<","<std::endl;
  }
};

int main()
{
  std::string name = "Tom";
  int age = 20;
  WiredEmployee a(name,age);
  std::cout<<"a: ";
  a.Print();

  WiredEmployee b(a);
  std::cout<<"b: ";
  b.Print();
  std::string name1 = "Jim";
  int age1 = 25;
  WiredEmployee c(name1, age1);
  std::cout<<"c: ";
  c.Print();
  c = a;

  return 0;
}

赋值运算符重载函数
从错误中可看出,赋值运算符重载函数没有生成。

若不想使用编译器自动生成的函数,需要自己明确地定义这些函数。

如果不想让用户使用这些函数,可以将其可访问范围设置为private。然而这样并不完全能够实现这种要求,因为对于友元和成员函数来说,它们仍然是可以访问的。

/***************************************
 * forbidden_use.cpp                   *
 *                                     *
 * C++禁止使用成员函数                 *
 ***************************************/

#include 

class Test
{
private:
  int i;

public:
  Test(int _i)
  {
    this->i = _i;
  }

  void Use()
  {
    /*成员函数中可用*/
    std::cout<<"成员函数:"<<std::endl;
    std::cout<<"  a:";
    Test a(10);
    a.Print();
    Test b(a);
    std::cout<<"  b:";
    b.Print();
    Test c(15);
    std::cout<<"  c:";
    c.Print();
    c = a;
    std::cout<<"  c:";
    c.Print();
  }

  void Print()
  {
    std::cout<<"i = "<std::endl;
  }
private:
  Test(Test& t)
  {
    i = t.i;
  }
  Test& operator=(const Test& t)
  {
    i = t.i;
    return *this;
  }

  friend void UseTest();
};

/*友元可用*/
void UseTest()
{
  std::cout<<"友元函数: "<<std::endl;
  Test a(15);
  std::cout<<"  a:";
  a.Print();

  Test b(a);
  std::cout<<"  b:";
  b.Print();

  Test c(20);
  std::cout<<"  c:";
  c.Print();

  c = a;
  std::cout<<"  c:";
  c.Print();

}

int main()
{
  Test a(20);
  a.Use();
  UseTest();

  /* 其他中不可用
    Test b(a);
    Test c(10);
    c = a;
   */

  return 0;
}

C++编译器为类自动生成的函数_第1张图片
更加稳妥的一种方法是将这些函数只声明为private,而不去实现它们。这样用户使用这些函数的错误将在编译时被发现,而成员函数或友元使用这些函数的错误将在链接时被发现。

/***************************************
 * forbidden_use_1.cpp                 *
 *                                     *
 * C++禁止使用成员函数                 *
 ***************************************/

#include 

class Test
{
private:
  int i;

public:
  Test(int _i)
  {
    this->i = _i;
  }

  void Use()
  {
    /*成员函数中不可用*/
    std::cout<<"成员函数:"<<std::endl;
    std::cout<<"  a:";
    Test a(10);
    a.Print();
    Test b(a);
    std::cout<<"  b:";
    b.Print();
    Test c(15);
    std::cout<<"  c:";
    c.Print();
    c = a;
    std::cout<<"  c:";
    c.Print();
  }

  void Print()
  {
    std::cout<<"i = "<std::endl;
  }
private:
  Test(Test& t);
  Test& operator=(const Test& t);

  friend void UseTest();
};

/*友元不可用*/
void UseTest()
{
  std::cout<<"友元函数: "<<std::endl;
  Test a(15);
  std::cout<<"  a:";
  a.Print();

  Test b(a);
  std::cout<<"  b:";
  b.Print();

  Test c(20);
  std::cout<<"  c:";
  c.Print();

  c = a;
  std::cout<<"  c:";
  c.Print();

}

int main()
{
  Test a(20);
  a.Use();
  UseTest();

  /* 其他中不可用
    Test b(a);
    Test c(10);
    c = a;
   */

  return 0;
}

全面禁用某成员

还有一种方法是定义一个基类,这个基类的这些函数设置为private,那么所有继承自该基类的子类就不能被用户,也不能被自己的成员函数和友元使用了。因为无论什么时候使用这些函数,它都会调用父类的相应函数,而父类的这些函数又是private,就会报错。

/***************************************
 * forbidden_use_2.cpp                 *
 *                                     *
 * C++禁止使用成员函数                 *
 ***************************************/

#include 

class Uncopyable
{
protected:
  Uncopyable(){};
  ~Uncopyable(){};

private:
  Uncopyable(const Uncopyable& );
  Uncopyable& operator=(const Uncopyable&);
};

class Test : private Uncopyable
{
private:
  int i;

public:
  Test(int _i)
  {
    this->i = _i;
  }

  void Use()
  {
    /*成员函数中不可用*/
    std::cout<<"成员函数:"<<std::endl;
    std::cout<<"  a:";
    Test a(10);
    a.Print();
    Test b(a);
    std::cout<<"  b:";
    b.Print();
    Test c(15);
    std::cout<<"  c:";
    c.Print();
    c = a;
    std::cout<<"  c:";
    c.Print();
  }

  void Print()
  {
    std::cout<<"i = "<std::endl;
  }

  friend void UseTest();
};

/*友元不可用*/
void UseTest()
{
  std::cout<<"友元函数: "<<std::endl;
  Test a(15);
  std::cout<<"  a:";
  a.Print();

  Test b(a);
  std::cout<<"  b:";
  b.Print();

  Test c(20);
  std::cout<<"  c:";
  c.Print();

  c = a;
  std::cout<<"  c:";
  c.Print();

}

int main()
{
  Test a(20);
  a.Use();
  UseTest();

  /* 其他中不可用
    Test b(a);
    Test c(10);
    c = a;
   */

  return 0;
}

下图中只显示了在成员函数Use中的错误,实际上,当把Use内的代码注释掉后,友元函数中的错误就会显示出来
C++编译器为类自动生成的函数_第2张图片

参考文献

  1. Scott Meyers著,侯捷译. Effective C++中文版. 电子工业出版社. 2012.

你可能感兴趣的:(C++)