博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
类Class(二):构造函数(constructors)
阅读量:6688 次
发布时间:2019-06-25

本文共 3790 字,大约阅读时间需要 12 分钟。

 构造函数(constructors)

 

对象(object)在生成过程中通常需要初始化变量或分配动态内存,以便我们能够操作,或防止在执行过程中返回意外结果。

例如,在前面的例子  中,如果我们在调用函数 set_values( ) 之前就调用了函数 area(),将会产生什么样的结果呢?

可能会是一个不确定的值,因为成员 width 和 height 还没有被赋于任何值。

 

为了避免这种情况发生,一个 class 可以包含一个特殊的函数:构造函数 constructor,它可以通过声明一个与 class 同名的函数来定义。

当且仅当要生成一个 class 的新的实例 (instance)的时候,也就是当且仅当声明一个新的对象,或给该 class 的一个对象分配内存的时候,

这个构造函数将自动被调用。下面,我们将实现包含一个构造函数的Rectangle class:

 

 

#include 
using namespace std;class Rectangle { int width, height; public: Rectangle (int, int); int area () {
return (width*height);}};Rectangle::Rectangle (int a, int b) { width = a; height = b;}int main () { Rectangle rect (3,4); Rectangle rectb (5,6); cout << "rect area: " << rect.area() << endl; cout << "rectb area: " << rectb.area() << endl; return 0;}
rect area: 12rectb area: 30

 

这个例子的输出结果与前面一个没有区别。在这个例子中,我们只是把函数 set_values 换成了class 的构造函数 constructor。注意这里参数是如何在 class 实例 (instance)生成的时候传递给构造函数的:

 

    Rectangle rect (3,4);

    Rectangle rectb (5,6);

 

同时你可以看到,构造函数的原型和实现中都没有返回值(return value),也没有 void 类型声明。构造函数必须这样写。一个构造函数永远没有返回值,也不用声明 void,就像我们在前面的例子中看到的

一致性初始化

 

上面调用构造函数的方式:Rectangle rect (3,4);  被称为函数形式(functional form)。除此之外,构造函数的调用还有其它的语法:

 

- 如果构造函数只有一个参数,调用时,可以用等式语法:

   class_name object_name = initialization_value; 

 

- 最近,C++ 介绍了另外一种调用语法:一致性初始化(Uniform initialization)。语法是把括号() 换成 大括号 {}

   class_name object_name { value, value, value, ... } 

    - object_name 和 大括号之间的等号可有可无

 

下面这个例子,使用4种方式调用构造函数:

#include 
using namespace std;class Circle { double radius; public: Cricle(double r) { radius = r; } double circum() { return 2*radius*3.14159265;}};int main () { Circle foo (10.0); //functional form Circle bar = 20.0; //等式语法 Circle baz {
30.0}; //uniform init Circle qux = {
40.0}; //带等式的 uniform init cout << "foo's circumference: " << foo.circum() << '\n'; return 0;}

 

使用一致性初始化的优势是:使用函数形式调用构造函数(使用括号()),会和函数调用方式混淆,但使用大括号{} 这种语法就不会了。并且这种语法会显示调用默认构造函数:

Rectangle rectb;   // default constructor calledRectangle rectc(); // function declaration (default constructor NOT called)Rectangle rectd{}; // default constructor called

 

关于默认构造函数,在这一篇里面有所提及。大部分开发者习惯使用函数形式调用构造函数,一些新的风格指南推荐使用大括号的方式。

 

 

构造函数中的成员初始化

 

使用构造函数初始化其他成员变量的时候,有两种方式:

  1. 在方法体(body)中对这些变量赋值,

  2. 通过在方法体前插入冒号(:),冒号后跟上一系列初始化值 (用逗号隔开)。这种方式被称为成员初始化(member initialization)

比如下面的例子:

class Rectangle {    int width,height;  public:    Rectangle(int,int);    int area() {
return width*height;}};

 

对构造函数声明后,接下来进行函数实现。按照 方式1 的做法是:

Rectangle::Rectangle(int x, int y) { width=x, height=y;}

 

方式2 的做法如下:

Rectangle::Rectangle(int x, int y) : width(x) {height=y;}

 

更激进一点:

Rectangle::Rectangle (int x, int y) : width(x), height(y) {};

 

使用成员初始化的情形

 

对于基本类型的初始化,上面两种方式没有区别。但是,如果某个成员变量是 类对象呢?这种成员变量在声明的时候,默认调用它的默认构造函数初始化的。这会产生两个问题:

  1. 假设类 A 的某个成员变量是 类对象 b,属于类B。如果 b 在 类A 的构造函数中被重新初始化了(比如重新赋值),那么之前 b 走的默认构造路线就是一种浪费

  2. 如果类B 此时没有默认构造函数怎么办?就会报错no matching function for call to 'B::B()'

 

通过冒号(:) 的形式使用 成员初始化 可以避免上面的问题

#include 
using namespace std;class Circle { double radius; public: Circle(double r) : radius(r) { } double area() {
return radius*radius*3.14159265;}};class Cylinder { Circle base; double height; public: Cylinder(double r, double h) : base (r), height(h) {} double volume() {
return base.area() * height;}};int main () { Cylinder foo (10,20); cout << "foo's volume: " << foo.volume() << '\n'; return 0;}

 

上面的代码,在类 Cylinder 中有类型是 Circle 的成员变量 base。类 Circle 没有默认构造函数,只有一个带参数的构造函数。在类 Cylinder 中,声明对象 base 的时候,它的默认构造函数就会被调用:

Circle base;

类 Cylinder 的构造函数又需要调用类 Circle 的构造函数去初始化 base,唯一的办法就是把初始化放在 成员初始化列表里面,也就是冒号(:) 后面的用逗号隔开的列表中。这样就避免了上面提到的问题。

 

成员初始化的语法中还可以使用一致性初始化语法(大括号{}):

Cylinder::Cylinder (double r, double h) : base{r}, height{h} { }

 

 

转载于:https://www.cnblogs.com/guozqzzu/p/3628756.html

你可能感兴趣的文章
【leetcode】955. Delete Columns to Make Sorted II
查看>>
JDK源码阅读-Integer
查看>>
Java修行之路
查看>>
接口(工厂模式&代理模式)
查看>>
3月个人随笔
查看>>
netty入门
查看>>
iOS 组件化流程详解(git创建流程)
查看>>
搜索关键字高亮显示,就比微信多个多音字搜索
查看>>
1303: [CQOI2009]中位数图
查看>>
在数组的开头插入键值对
查看>>
LTTng
查看>>
常用模块
查看>>
HTTPS = HTTP + SSL
查看>>
Copy修饰的NSArray
查看>>
eclipse新建web项目
查看>>
gnuplot
查看>>
GraphQL(三):GraphQL集成SpringBoot原理
查看>>
Balloons
查看>>
posix消息队列(1)
查看>>
using for jekyll
查看>>