构造函数

当定义了一个整型变量:

  int a;

这会申请了一块内存空间来存储a,但是这块内存中原本有数据的,可能是任何值,这不是你所希望的,若你就希望a表示1,所以要把a的值赋值为1。

  int a = ;

例:

#include <iostream>
using namespace std;
class Date
{
int d, m, y;
public:
void init(int dd, int mm, int yy)
{
d = dd;
m = mm;
y = yy;
};
void add_year(int n)
{
y+=n;
}
void add_month(int n)
{
m+=n;
}
void add_day(int n)
{
d+=n;
}
void show()
{
cout<<y<<" "<<m<<" "<<d;
}
};
int main( )
{
Date today;
today.init(,,);
Date tomorrow = today;
tomorrow.add_day();
tomorrow.show();
return ;
}

程序执行结果为:
    2011 11 11
若对对象未进行初始化,则:

int main( )
{
Date today;
// today.init(12,12,2001);
Date tomorrow = today; // warning C4700: 使用了未初始化的局部变量“today”
tomorrow.add_day();
tomorrow.show();
return ;
}

程序执行结果为:
-858993460 -858993460 -858993459
可见,程序设计的悲哀是对不确定的状态进行了确定的操作。

一个好办法是允许程序员声明一个函数显示地(the explict purpose)去初始化(initializing)对象。类的对象是这个类的一个实例,也称为类变量。和基本数据类型变量一样,也可以为其数据成员赋初值,不过对象的初始化比较复杂,其中最重要的方式就是构造函数(constructor)。

类的数据成员是不能在声明类时初始化的。

class Date
{
int d = ; // error C2864: “Date::d”: 只有静态常量整型数据成员才可以在类中初始化
int m = ; // error C2864: “Date::d”: ……
int y = ; // error C2864: “Date::d”: ……
}

如果一个类中所有的成员都是公用的,如:

如果一个类中所有的成员都是公用的,如:
class Date
{
public:
int d, m, y;
}
Date today = {,,}; // 将today初始化为d:11,m:11,y:2011

这和结构体变量的初始化是差不多的,但这种方法无法初始化私有成员。

class Date
{
private:
int d, m, y;
}
Date today = {,,}; // error C2552:“today”: 不能用初始值设定项列表初始化非聚合

如果数据成员是私有的,该如何进行初始化?可以通过类的一个公有接口来进行“初始化”,即调用一个公共的成员函数来“初始化”,严格地说,这是赋值而不是初始化。

最好的方法是通过构造函数来进行初始化,类的构造函数由编译器自动调用,而不是由程序员调用。
它承担的任务是:实例(对象)的生成与初始化。构造函数是类中的一种特殊函数,当一个类被创建时自动被调用。
构造函数用于初始化数据成员和执行其它与创建对象有关的合适的处理过程。
构造函数是一个与其所在的类同名的函数。

class Date
{
int d, m, y;
public:
Date(int dd, int mm, int yy) // 构造函数,初始化类的私有成员d、m、y
{
d = dd;
m = mm;
y = yy;
};
};

构造函数大体可分为两类:
(1)缺省构造函数(the default constructor),无调用参数。
(2)参数化的构造函数(the parameterized constructor),有调用参数。

class Person
{
public:
Person( ); // 缺省构造函数
Person( const string& n ); // 复制构造函数
Person(......); // 参数化的构造函数
private:
string name;
};

构造函数没有返回值,连void也不行。

class Person
{
public:
void Person( ); // error
};

编译上边的Person类,会出现下面的错误提示:
    error C2380: “Person”前的类型(构造函数有返回类型或是当前类型名称的非法重定义?)
类的构造函数可以被重载(be overloaded)。但是, 每个构造函数必须有不同的函数签名。当类的一个实例创建时,一个合适的构造函数被自动调用。一个类中可以根据需要定义多个构造函数,编译程序根据调用时实参的数目、类型和顺序自动找到与之匹配者。

class Date
{
int d, m, y;
public:
Date(int dd, int mm, int yy);
Date(int dd, int mm); // today's year
Date(int dd); // today's month and year
Date( ); // default Date: today
Date(const char* p); // date in string representation
/* ... */
};
void main( )
{
Date today(); // 调用Date(int dd)
Date today(, ); // 调用Date(int dd, int mm)
Date july4("November 11, 2011"); // 调用Date(const char* p)
Date now; // 调用Date( )
/* ...*/
}

 

在实际程序设计中,有时很难估计将来对构造函数形参的组合会有怎样的要求,一种有效的策略是对构造函数也声明有省缺值的形参(Default Arguments)。

例:

class Date
{
int d, m, y;
public:
Date(int dd = , int mm = , int yy = );
/* ... */
};
int main( )
{
Date today(); // dd = 11, int mm = 0, int yy = 0
Date someDay(, ); // dd = 11, int mm = 11, int yy = 0
Date aDay(, , ); // dd = 11, int mm = 11, int yy = 2011
Date now; // dd = 0, int mm = 0, int yy = 0
/* ...*/
return ;
}

在创建一类的对象数组时,对于每一个数组元素,都会执行缺省的构造函数。

#include <iostream>
using namespace std;
unsigned count = ;
class A
{
public:
A ( )
{
cout << "Creating A " << ++count <<endl;
}
};
int main( )
{
A ar[]; // 对象数组
return ;
}

执行结果为:
"Creating A " 1
"Creating A " 2
"Creating A " 3

通常将构造函数的声明置于public中,假如将其放入private区段中会发生什么后果?这将会将构造函数成为私有的,那将意味什么?

例:将构造函数声明为private。

class Date
{
private:
int d, m, y;
Date( )
{
d = ;
m = ;
y = ;
}
};
int main( )
{
Date today; // error C2248: “Date::Date”: 无法访问 private 成员(在“Date”类中声明)
return ;
}

在上例中,将默认构造函数声明成private,这样便限制了无参数的Date对象创建。我们知道,当我们在程序中声明一个对象时,编译器为调用构造函数(如果有的话),而这个调用将通常是外部的,也就是说它不属于class对象本身的调用,假如构造函数是私有的,由于在class外部不允许访问私有成员,所以这将导致编译出错。若构造函数声明成private,可以使用该类的友元函数或者友元类创建其对象,详见后面友元部分。

例:这里举一个通过构造函数来限制类对象创建的例子。(Restricting Object Creation Through Constructor)

class Emp
{
public:
Emp( unsigned ID )
{
id = ID;
}
//…
private:
unsigned id;
// Emp( );
}; int main( )
{
Emp elvis;// Error: no public default constructor
Emp cher( );
return ;
}

除以下两种情况外,编译器为一个类提供一个public型的缺省构造函数。
(1)如果一个类声明任何一个构造函数,则编译器不提供 public型的缺省构造函数。 如果需要有一个缺省构造函数的话,程序员必须自己编写一个缺省构造函数。
(2)如果一个类声明了一个非公有的缺省构造函数,则编译器不提供缺省构造函数(如上例)。

The compiler provides a public default constructor for a class with two exception:
If a class explicitly declare any constructor, the complier does not provide a public default constructor. In this case, the programmer must provide a public default constructor if desired.
If a class declares a nonpublic default constructor, the complier does not provide a public default constructor.

class Date
{
int d, m, y;
public:
Date(int dd, int mm, int yy)
{
d = dd;
m = mm;
y = yy;
}
};
int main( )
{
Date today = Date(,,); //OK
Date this_day(,,); //OK
Date my_birthday; // error C2512: “Date”: 没有合适的默认构造函数可用
/* ...*/
return ;
}

有一个有参数化的构造函数Date(int dd, int mm, int yy),编译器将不再提供默认的构造函数,因此创建Date对象必须给出3个int型参数,而无参数的my_birthday无法创建。

C++ 类 构造函数 constructor的更多相关文章

  1. JS面向对象(1) -- 简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式

    相关链接: JS面向对象(1) -- 简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式 JS面向对象(2) -- this的使用,对 ...

  2. javascript工厂函数(factory function)vs构造函数(constructor function)

    如果你从其他语言转到javascript语言的开发,你会发现有很多让你晕掉的术语,其中工厂函数(factory function)和构造函数(constructor function)就是其中的一个. ...

  3. C++中虚继承派生类构造函数的正确写法

    最近工作中某个软件功能出现了退化,追查下来发现是一个类的成员变量没有被正确的初始化.这个问题与C++存在虚继承的情况下派生类构造函数的写法有关.在此说明一下错误发生的原因,希望对更多的人有帮助. 我们 ...

  4. WorldWind源码剖析系列:BMNG类构造函数深入分析

    BMNG构造函数深入分析 一.主要类图 二.主要功能: 1)        BMNG类 BMNG类将包含以“Blue Marble”为主题的所有可渲染影像的根节点添加到当前星球的可渲染对象列表中,包括 ...

  5. C#中派生类调用基类构造函数用法分析

    这里的默认构造函数是指在没有编写构造函数的情况下系统默认的无参构造函数 1.当基类中没有自己编写构造函数时,派生类默认的调用基类的默认构造函数例如: ? 1 2 3 4 5 6 7 8 9 10 11 ...

  6. 转 关于C#中派生类调用基类构造函数的理解

    关于C#中派生类调用基类构造函数的理解 .c#class       本文中的默认构造函数是指在没有编写构造函数的情况下系统默认的无参构造函数 1.  当基类中没有自己编写构造函数时,派生类默认的调用 ...

  7. C++类构造函数初始化列表

    C++类构造函数初始化列表 构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式.例如: class CExample {public:     ...

  8. C# 类构造函数赋值里属性与字段赋值注意项

    public class Test { public Test(int age) { this.Age=age;//如果这里使用的是this.age=age;那么属性里的判断将不会执行 } priva ...

  9. C++类构造函数初始化列表(转)

    构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式.例如: { public:     int a;     float b;     //构 ...

随机推荐

  1. 利用SimpleDateFormat进行时间的跨时区转换 - Java

    * 次方法主要用来将特定时区的时间转换成指定时区的时间,比如将北京时间“2018-04-08 15:40:49.031”,转换对应的美国东部时间是“2018-04-08 03:40:49.031”   ...

  2. 【LeetCode415】Add Strings

    题目描述: 解决思路: 此题较简单,和前面[LeetCode67]方法一样. Java代码: public class LeetCode415 { public static void main(St ...

  3. 数据结构与算法之Stack(栈)的应用——in dart

    参考教科书上的一个应用例子,用栈来分析一行输入中的括号brackets是否匹配.用stdin读取用户输入,并输出检查结果.exit 退出. 注意这行代码: import 'stack.dart';// ...

  4. 大数据入门第一天——基础部分之Linux基础(环境准备与先导知识)

    一.Linux环境安装 1.VM的安装 参考Linux环境搭建随笔:http://www.cnblogs.com/jiangbei/p/7248054.html 2.CentOS的安装 同参考上述随笔 ...

  5. 时间戳转为C#格式时间

    经常发现很多地方使用一个时间戳表示时间.比如: 1370838759 表示 2013年6月10日 12:32:39. 我们就需要一个工具,方便地转换这种时间格式 什么是时间戳? 时间戳, 又叫Unix ...

  6. WebX框架的页面授权

    WebX框架的页面授权 什么是页面授权,简单来说就是对于一个Web应用程序里,哪些页面可以被哪些人在什么情况下访问进行限制.举个简单的例子,有些页面只有用户登录以后才能访问,而另外一些页面无论是否用户 ...

  7. 确定有限自动机 valid number

    原题地址:http://oj.leetcode.com/problems/valid-number/ 题意:判断输入的字符串是否是合法的数. 解题思路:这题只能用确定有穷状态自动机(DFA)来写会比较 ...

  8. WebUploader在IE9中文件选择按钮点击没反应

    一.问题: 最近做的公司项目里,用户环境一直用的火狐,但是实际的用户群体都是银行人员 政府部门怎么也要用 IE,而且还有一些用的IE版本是古董版本IE9 IE9 相比 IE8 多了图像渲染等,无法兼容 ...

  9. EDB*Plus的当前路径问题

    磨砺技术珠矶,践行数据之道,追求卓越价值 回到上一级页面: PostgreSQL基础知识与基本操作索引页     回到顶级页面:PostgreSQL索引页 [作者 高健@博客园  luckyjackg ...

  10. Gitlab+Jenkins学习之路(十二)之Maven的私有仓库Nexus

    1.什么是Nexus? 在前面进行maven项目的构建中,可以看到在构建的过程中需要安装maven的依赖插件,如图: 而在maven的默认配置中是在官网的中央仓库和第三方的maven仓库进行下载,速度 ...