C++标准ISO-IEC-14882-2003:第12章:类的特殊成员函数-第1节:构造函数
2009-06-17 20:14
288 查看
12.
特殊成员函数
【按:以下简称特殊函数或这些函数;如没有特别说明,对象是指类实例化的对象, 最外层的对象是指最继承子对象】
特殊成员函数包括默认构造函数、拷贝构造函数、拷贝赋值操作符和析构函数。除了
12.1
中的情况,当程序不显式地声明这些函数时,编译器会隐式声明它们;如果使用了它们,编译器还会隐式地定义这些函数,如
12.1
,
12.4
和
12.8
所示。不能够在程序中定义隐式声明的特殊函数。
【按:要么不自定义,要自定义就得自己声明】
程序中可以显式地使用隐式声明的特殊函数。
【例:程序中可以显式调用这些函数、对其取地址、或是构造一个指向这些函数的指针。
struct A {};
//
隐式地定义了
A::operator=
函数
struct B: A
{
B& operator = (const B&);
};
B&
B::operator = (const B& s)
{
this->A::operator=(s);
//
合法
return *this;
}
】【注:特殊成员函数影响对象的创建、拷贝、销毁以及值的类型转化的方式。通常这些函数是被隐式地调用的。】
特殊成员函数遵循普通成员函数的访问权限控制。【例如:将一个类的构造函数声明为
protected
,就能保证只有派生类或友元可以使用这个构造函数进而实例化该类的对象。】
12.1
构造函数
1.
构造函数没有名字。声明或定义构造函数的时候需要一种特殊的语法:函数说明符(可选)
+
类名
+
参数列表。在这个语法中,括住类名的圆括号(可选)会被忽略。
【按:函数说明符一共有3
个:inline, virtual, explicit
,其中virtual
在这里不允许使用】
2.
构造函数是用来初始化该类的对象的。因为没有名字,所以在名字查找
过程中构造函数永远不会被涉及到;然而使用函数式的显式类型转换
【按:即类似C
语言的强制类型转换语法】
却会导致构造函数被调用从而进行对象初始化。【注:对象初始化参见
12.6
节】
3.
命名一个类的
typedef-name
也是个类名,但是这个
typedef-name
却不能用作构造函数的标识符。
4.
构造函数不能为
virtual
或
static
。构造函数可以被
const
、
volatile
或
const volatile
的对象调用。构造函数不能声明为
const
、
volatile
或
const volatile
类型的成员,因为
const
和
volatile
的语义对于一个正在构建的对象来说是不适用的,这些语义只有在最外层的对象构建完毕之后才生效。
5.
类
X
的默认构造函数是
X
的不带任何参数的构造函数。如果用户没有为
X
声明任何构造函数,默认构造函数就会被隐式地声明。隐式声明的默认构造函数的属性是
inline
、
public
的。当隐式声明的默认构造函数满足以下条件时,它被称为是
trivial
的:
【按: trivial
是无关痛痒、可忽略的意思】
a)
类没有虚函数,没有虚基类
b)
所有直接基类都具有
trivial
的构造函数
c)
对于所有非静态数据成员来说,如果它是类(或对象数组),那每个这样的数据成员都得有
trivial
的构造函数
6.
否则,构造函数就是非
trivial
的。
7.
当用隐式声明的默认构造函数来创建一个对象的时候,它会被隐式地定义。该构造函数执行的初始化操作与用户写的空默认构造函数(初始化列表为空,函数体为空)完全相同。如果拿用户写的这个构造函数替换掉隐式定义的默认构造函数之后发现是非法的,那么替换之前的程序也必定是非法的。在隐式定义的默认构造函数被隐式定义之前,该类的所有基类的默认构造函数、该类的所有非静态数据成员的构造函数
都必须已经定义好。【注:隐式声明的默认构造函数还会有一个异常说明。】默认构造函数被用来:
a)
创建没有初值的静态或自动存储类型对象;
b)
创建省略
new-initializer
的
new
表达式创建的动态存储类型对象;
c)
显式类型转换时调用。
8.
如果默认构造函数被使用了,但是该函数却由于权限的缘故而无法访问,那么程序就是非法的。
9.
【注:
12.6.2
描述了基类构造函数和非静态数据成员的构造函数
的调用顺序以及调用这些构造函数时参数该如何传递】
10.
类
X
的拷贝构造函数是个参数为
X&
或
const X&
的构造函数
11.
union
的成员如果是个类,那么这个类的构造函数必须是
trivial
的
12.
构造函数永远都不能有返回类型,甚至连返回
void
类型都不允许。在构造函数体中,可以用
return
语句,但不能返回任何值。不要试图去取构造函数的地址。
13.
函数式的显式类型转换会创建一个该类的(临时)对象。【注:语法看起来有点儿像构造函数的显式调用】这样创建的对象是没有名字的。【注:显式的构造函数调用永远不会生成左值】
14.
【注:上述第
13
条的语法如果在构造函数中使用,那么在某些清况下它有特殊的语义:
12.6.2
,
12.7
】
【按:用在初始化列表中,用来初始化基类子对象】
15.
在
const
对象的创建过程中,如果该对象或子对象的值被作为一个左值使用,而该左值却又不是通过
this
指针获得的(比如直接使用该对象的名字),那么这样获得的值是未定义的。【例:
struct C;
void no_opt(C*);
struct C
{
int c;
C() : c(0) {no_opt(this);}
};
const C cobj;
void no_opt(C*
cptr)
{
int i = cobj.c * 100;
//
该函数在
cobj
的构造函数中被调用,这里是通过
cobj
而不是
this
获取
c
的值(是个左值,然后自动转化为右值),所以该值是未定义的
cptr->c = 1;
cout << i << endl;
cout << cobj.c * 100 <<
endl;
};
特殊成员函数
【按:以下简称特殊函数或这些函数;如没有特别说明,对象是指类实例化的对象, 最外层的对象是指最继承子对象】
特殊成员函数包括默认构造函数、拷贝构造函数、拷贝赋值操作符和析构函数。除了
12.1
中的情况,当程序不显式地声明这些函数时,编译器会隐式声明它们;如果使用了它们,编译器还会隐式地定义这些函数,如
12.1
,
12.4
和
12.8
所示。不能够在程序中定义隐式声明的特殊函数。
【按:要么不自定义,要自定义就得自己声明】
程序中可以显式地使用隐式声明的特殊函数。
【例:程序中可以显式调用这些函数、对其取地址、或是构造一个指向这些函数的指针。
struct A {};
//
隐式地定义了
A::operator=
函数
struct B: A
{
B& operator = (const B&);
};
B&
B::operator = (const B& s)
{
this->A::operator=(s);
//
合法
return *this;
}
】【注:特殊成员函数影响对象的创建、拷贝、销毁以及值的类型转化的方式。通常这些函数是被隐式地调用的。】
特殊成员函数遵循普通成员函数的访问权限控制。【例如:将一个类的构造函数声明为
protected
,就能保证只有派生类或友元可以使用这个构造函数进而实例化该类的对象。】
12.1
构造函数
1.
构造函数没有名字。声明或定义构造函数的时候需要一种特殊的语法:函数说明符(可选)
+
类名
+
参数列表。在这个语法中,括住类名的圆括号(可选)会被忽略。
【按:函数说明符一共有3
个:inline, virtual, explicit
,其中virtual
在这里不允许使用】
2.
构造函数是用来初始化该类的对象的。因为没有名字,所以在名字查找
过程中构造函数永远不会被涉及到;然而使用函数式的显式类型转换
【按:即类似C
语言的强制类型转换语法】
却会导致构造函数被调用从而进行对象初始化。【注:对象初始化参见
12.6
节】
3.
命名一个类的
typedef-name
也是个类名,但是这个
typedef-name
却不能用作构造函数的标识符。
4.
构造函数不能为
virtual
或
static
。构造函数可以被
const
、
volatile
或
const volatile
的对象调用。构造函数不能声明为
const
、
volatile
或
const volatile
类型的成员,因为
const
和
volatile
的语义对于一个正在构建的对象来说是不适用的,这些语义只有在最外层的对象构建完毕之后才生效。
5.
类
X
的默认构造函数是
X
的不带任何参数的构造函数。如果用户没有为
X
声明任何构造函数,默认构造函数就会被隐式地声明。隐式声明的默认构造函数的属性是
inline
、
public
的。当隐式声明的默认构造函数满足以下条件时,它被称为是
trivial
的:
【按: trivial
是无关痛痒、可忽略的意思】
a)
类没有虚函数,没有虚基类
b)
所有直接基类都具有
trivial
的构造函数
c)
对于所有非静态数据成员来说,如果它是类(或对象数组),那每个这样的数据成员都得有
trivial
的构造函数
6.
否则,构造函数就是非
trivial
的。
7.
当用隐式声明的默认构造函数来创建一个对象的时候,它会被隐式地定义。该构造函数执行的初始化操作与用户写的空默认构造函数(初始化列表为空,函数体为空)完全相同。如果拿用户写的这个构造函数替换掉隐式定义的默认构造函数之后发现是非法的,那么替换之前的程序也必定是非法的。在隐式定义的默认构造函数被隐式定义之前,该类的所有基类的默认构造函数、该类的所有非静态数据成员的构造函数
都必须已经定义好。【注:隐式声明的默认构造函数还会有一个异常说明。】默认构造函数被用来:
a)
创建没有初值的静态或自动存储类型对象;
b)
创建省略
new-initializer
的
new
表达式创建的动态存储类型对象;
c)
显式类型转换时调用。
8.
如果默认构造函数被使用了,但是该函数却由于权限的缘故而无法访问,那么程序就是非法的。
9.
【注:
12.6.2
描述了基类构造函数和非静态数据成员的构造函数
的调用顺序以及调用这些构造函数时参数该如何传递】
10.
类
X
的拷贝构造函数是个参数为
X&
或
const X&
的构造函数
11.
union
的成员如果是个类,那么这个类的构造函数必须是
trivial
的
12.
构造函数永远都不能有返回类型,甚至连返回
void
类型都不允许。在构造函数体中,可以用
return
语句,但不能返回任何值。不要试图去取构造函数的地址。
13.
函数式的显式类型转换会创建一个该类的(临时)对象。【注:语法看起来有点儿像构造函数的显式调用】这样创建的对象是没有名字的。【注:显式的构造函数调用永远不会生成左值】
14.
【注:上述第
13
条的语法如果在构造函数中使用,那么在某些清况下它有特殊的语义:
12.6.2
,
12.7
】
【按:用在初始化列表中,用来初始化基类子对象】
15.
在
const
对象的创建过程中,如果该对象或子对象的值被作为一个左值使用,而该左值却又不是通过
this
指针获得的(比如直接使用该对象的名字),那么这样获得的值是未定义的。【例:
struct C;
void no_opt(C*);
struct C
{
int c;
C() : c(0) {no_opt(this);}
};
const C cobj;
void no_opt(C*
cptr)
{
int i = cobj.c * 100;
//
该函数在
cobj
的构造函数中被调用,这里是通过
cobj
而不是
this
获取
c
的值(是个左值,然后自动转化为右值),所以该值是未定义的
cptr->c = 1;
cout << i << endl;
cout << cobj.c * 100 <<
endl;
};
相关文章推荐
- C++标准ISO-IEC-14882-2003:第12章:类的特殊成员函数-第6节:初始化
- C++标准ISO-IEC-14882-2003:第12章:类的特殊成员函数-第7节:构造与析构
- C++标准ISO-IEC-14882-2003:第12章:类的特殊成员函数-第3节:类型转换
- C++标准ISO-IEC-14882-2003:第14章:模板-第1节:模板的参数
- C++标准ISO-IEC-14882-2003:第9章:类 -第1节:类名
- C++标准ISO-IEC-14882-2003:第9章:类-第2节:类的成员
- C++标准ISO-IEC-14882-2003:第12章:类的特殊成员函数-第5节:自由存储管理
- C++标准ISO-IEC-14882-2003:第12章:类的特殊成员函数-第8节:拷贝对象
- C++标准ISO-IEC-14882-2003:第14章:模板-第3节:模板实参
- C++标准ISO-IEC-14882-2003:第12章:类的特殊成员函数-第2节:临时对象
- C++标准ISO-IEC-14882-2003:第14章:模板-第2节:模板特化的名字
- C++标准ISO-IEC-14882-2003:第14章:模板
- C++标准ISO-IEC-14882-2003:第12章:类的特殊成员函数-第4节:析构函数
- 3.10 左值和右值 中英文对照(C++标准中文版 ISO/IEC 14882:2014)
- 4.1 左值到右值的转换 中英文对照(C++标准中文版 ISO/IEC 14882:2014)
- ISO/IEC 14882:2011之条款4——标准转换
- C++标准ISO-IEC-14882-2003:第14章:模板-第4节:类型等价
- C++构造函数初始化类的特殊成员变量——类
- ISO/IEC 14882:2011之条款5.2——后缀表达式
- ISO/IEC 14882:2011之条款2——词法协定