码迷,mamicode.com
首页 > 编程语言 > 详细

c++ 构造函数

时间:2015-06-02 09:34:39      阅读:165      评论:0      收藏:0      [点我收藏+]

标签:

    构造函数是特殊的成员函数, 只要创建类类型的新对象, 都要执行构造函数. 构造函数的工作是保证每个对象的数据成员具有合适的初始值. 构造函数的名字与类名相同, 并且不能指定返回类型. 像其他任何函数一样, 它们可以没有形参, 也可以定义多个形参. 用一个例子来具体介绍.

   

<span style="font-size:14px;">class Sales_item
{
  public:
     Sales_item() : units_sold(0), revenue(0.0) { }
  private:
      std::string isbn;  //国际标准书号
      unsigned units_sold; // 销售的书量
      double revenue;  //收入
};</span>
这个构造函数使用构造函数初始化列表来初始化units_sold和revenue成员. isbn成员由string的默认构造函数隐式初始化为空串. 

每当创建一个Sales_item的对象时, 都会调用默认构造函数对数据成员初始化, 上述代码运行结果如图:

技术分享

构造函数初始化列表以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个数据成员后面跟一个放在圆括号中的初始化表达式. 构造函数可以定义在类的内部或外部.构造函数初始化只在构造函数的定义中而不是声明中指定.



1.构造函数可以被重载

  可以为一个类声明的构造函数没有限制,只要每个构造函数的形参表是唯一的. 为上述代码添加一个接受一个string实参构造函数


<pre name="code" class="cpp"><span style="font-size:14px;">#include <iostream>
#include <string>
using namespace std;
class Sales_item
{
  public:
     Sales_item() : units_sold(0), revenue(0.0) { }
     Sales_item(const string& S);
     void Display(); //显示数据成员
     
  private:
      string isbn;  //国际标准书号
      unsigned units_sold;// 销售的书量
      double revenue; // 收入
};
 
void Sales_item:: Display()
{
 cout<<"isbn: "<<isbn<<endl;
 cout<<"units_sold: "<<units_sold<<endl;
 cout<<"revenue: "<<revenue<<endl;
}
Sales_item::Sales_item(const string& S)
{
  isbn=S;
  units_sold=0;
  revenue=0;
}



int main()
{
  Sales_item A;
  Sales_item B("haha");
  cout<<"默认构造函数: "<<endl;
  A.Display();
  cout<<"带string形参的构造函数:"<<endl;
  B.Display();
  
  return 0;
}
</span>



技术分享

2.实参决定使用哪个构造函数

3.构造函数自动执行

只要创建该类型的一个对象,编译器就运行一个构造函数.

4.构造函数不能声明为const

从概念上讲, 可以认为构造函数分为两个阶段执行: (1) 初始化阶段  (2)普通的计算阶段.  

计算阶段由构造函数体中的所有语句组成.

不管成员是否在构造函数初始化列表中显示初始化, 类类型的数据成员总是在初始化阶段初始化. 初始化发生在计算阶段开始之前.

<span style="font-size:14px;">Sales_item::Sales_item(const string& S)
{
  isbn=S;
  units_sold=0;
  revenue=0;
}
</span>

在执行上面的构造函数之前, 要初始化isbn成员. 这个构造函数隐式使用默认的string构造函数来初始化isbn. 执行构造函数的函数体时, isbn成员已经有值了. 该值被构造函数函数体中的赋值所覆盖.

在我们编程中, 最好养成用初始化列表初始化成员的好习惯.  effective c++条款4中说道,  构造函数最好使用成员初值列, 而不要在构造函数本体内使用赋值操作.
对于数据成员是内置类型的来说, 初始化列表 和 使用赋值操作 并没有差别,  但是对于像Sales_item类这样数据成员中有类类型成元的类来说, 不使用初始化列表会多调用一个string类重载赋值运算符, 导致更高的成本.

注:有些成员必须在构造函数初始化列表中进行初始化. 对于这样的成员, 在构造函数体中对它们赋值不起作用.  没有默认构造函数的类类型的成员, 以及const或引用类型的成员, 不管是哪种类型, 都必须在构造函数初始化列表中进行初始化 . 因为可以初始化const对象或引用类型的对象, 但不能对它们赋值.初始化const或引用类型数据成员的唯一机会是在构造函数初始化列表中.

养成使用初始化列表的好习惯, 就不用担心这些问题啦.


成员初始化的次序
   构造函数中, 成员被初始化的次序就是定义成员的次序. 第一个成员首先被初始化, 然后类推.
考虑下面的类:
<span style="font-size:14px;">class X {
  int i;
  int j;
 public:
   X(int val) : j(val), i(j) {}
};</span>

上述代码会报错, 因为i首先被初始化, 这个初始化列表的效果是用尚未初始化的j值来初始化i !


默认实参与构造函数
class Sales_item
{
   public:
     Sales_item( const std::string &book = " ") : ibsn(book), units_sold(0), revenue(0.0) {}
      Sales_item( std::istream &is)
};

对于下面的任意一种定义, 将执行其为 string形参接受默认实参的那个构造函数:
  Sales_item empty;
  Sales_item Primer_3rd_ED ("0-201-82470-1");

使用默认实参, 可以减少代码重复.

默认构造函数
  只要定义一个对象时没有提供初始化式, 就使用默认构造函数. 为所有形参提供默认实参的构造函数也定义了默认构造函数.

1. 合成的默认构造函数
   一个类哪怕只定义了一个构造函数, 编译器也不会再生成默认构造函数, 只有当一个类没有定义构造函数时, 编译器才会自动生成一个默认构造函数.  但是其实类如果没有定义构造函数, 编译器也不一定会提供一个默认构造函数.
有四种情况编译器会为合成默认构造函数
1:含有默认/构造函数的成员类对像
2:带有默认/构造函数的基类对象
3: 含有虚函数的类
4:继承虚基类的类

   合成的默认构造函数使用与变量初始化相同的规则来初始化成员. 具有类类型的成员通过运行各自的默认构造函数来进行初始化. 内置和复合类型的成员, 如指针和数组, 只对定义在全局作用域中的对象才初始化. 当对象定义在局部作用域时, 内置或复合类型的成员不进行初始化.
  如果类包含内置或复合类型的成员, 则类不应该依赖于合成的默认构造函数, 它应该定义自己的构造函数来初始化这些成员.

类通常应该定义一个默认构造函数


隐式类型转换
    

<span style="font-size:14px;">class Sales_item
{
   public:
     Sales_item( const std::string &book = " ") : ibsn(book), units_sold(0), revenue(0.0) {}
      Sales_item( std::istream &is)
};</span>

这里的每个构造函数都定义了一个隐式转换, 因此, 在期待一个Sales_item 类型对象的地方, 可以使用一个string 或一个 istream;

string null_book = "9-999-99999-9";
item.same_isbn(null_book);

这段程序使用一个string类型对象作为实参传给Sales_item 的 same_isbn函数. 该函数希望一个Sales_item 对象作为实参. 编译器使用接受一个 string 的 Sales_item 构造函数从null_book 生成一个新的 Sales_item 对象. 新生成的(临时的) Sales_item被传递给same_isbn .

但是隐式转换通常并不是我们想要的, 并且可能会导致让我们意象不到的错误.  可以通过将构造函数声明为explicit来防止隐式转换.

<span style="font-size:14px;">class Sales_item
{
   public:
     explicit  Sales_item( const std::string &book = " ") : ibsn(book), units_sold(0), revenue(0.0) {}
     explicit   Sales_item( std::istream &is)
};</span>

现在, 两个构造函数都不能用于隐式创建对象. 前两个使用都不能编译:
item.same_isbn(null_book); // error:  string constructor is explicit
item.same_isbn(cin);  //error:  istream constructor is explicit

注: 通常, 单形参构造函数应该为explicit, 将构造函数设置为 explicit 可以避免错误, 并且当转换有用时, 用户可以显示地构造对象.


c++ 构造函数

标签:

原文地址:http://blog.csdn.net/nizhannizhan/article/details/46315981

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!