复制机关函数
添加时间:2013-7-18 点击量:
0. 复制机关函数
只有单个形参,并且该形参是对本类类型对象的引用(常用 const 润饰),如许的机关函数称为复制机关函数。
与默认机关函数一样,复制机关函数可由编译器隐式调用。
复制机关函数可用于:
按照另一个同类型的对象显式或隐式初始化一个对象。
复制一个对象,将它作为实参传给一个函数。
从函数返回时复制一个对象。
初始化次序容器中的元素。
按照元素初始化式列表初始化数组元素。
对象的定义情势
回想一下,C++ 支撑两种初始化情势:
直接初始化和复制初始化。复制初始化应用 = 符号,而直接初始化将初始化式放在圆括号中。
当用于类类型对象时,初始化的复制情势和直接情势有所不合:
直接初始化直接调用与实参匹配的机关函数,复制初始化老是调用复制机关函数。
复制初始化起首应用指定机关函数创建一个姑且对象,然后用复制机关函数将那个姑且对象复制到正在创建的对象:
string null_book = 9-999-99999-9; // copy-initialization
string dots(10, .); // direct-initialization
string empty_copy = string(); // copy-initialization
string empty_direct; // direct-initialization
对于类类型对象,只有指定单个实参或显式创建一个姑且对象用于复制时,才应用复制初始化。
创建 dots 时,调用参数为一个数量和一个字符的 string 机关函数并直接初始化 dots 的成员。
创建 null_book 时,编译器起首调用接管一个 C 风格字符串形参的 string 机关函数,创建一个姑且对象,
然后,编译器应用 string 复制机关函数将 null_book 初始化为那个姑且对象的副本。
empty_copy 和 empty_direct 的初始化都调用默认机关函数。
对前者初始化时,默认机关函数函数创建一个姑且对象,然后复制机关函数用该对象初始化 empty_copy。
对后者初始化时,直接运行 empty_direct 的默认机关函数。
支撑初始化的复制情势主如果为了与 C 的用法兼容。
当景象容许时,可以容许编译器跳过复制机关函数直接创建对象,但编译器没有任务如许做。
凡是直接初始化和复制初始化仅在初级别上存在差别。
然而,对于不支撑复制的类型,或者应用非 explicit 机关函数的时辰,它们有本质差别:
ifstream file1(filename); // ok: direct initialization
ifstream file2 = filename; // error: copy constructor is private
// This initialization is okay only if
// the Sales_item(const string&) constructor is not explicit
Sales_item item = string(9-999-99999-9);
ifstream 类定义了一个可用 C 风格字符串调用的机关函数,应用该机关函数初始化 file1。
看上去等效的 file2 初始化应用复制初始化,但该定义不正确。
因为不克不及复制 IO 类型的对象,所以不克不及对那些类型的对象应用复制初始化。
item 的初始化是否正确,取决于正在应用哪个版本的 Sales_item 类。
某些版本将参数为一个 string 的机关函数定义为 explicit。
若是机关函数是显式的,则初始化失败;若是机关函数不是显式的,则初始化成功。
形参与返回值
正如我们所知,当形参为非引用类型的时辰,将复制实参的值。
类似地,以非引用类型作返回值时,将返回 return 语句 中的值的副本。
当形参或返回值为类类型时,由复制机关函数进行复制。例如,推敲之前写过的 make_plural 函数:
// copy constructor used to copy the return value;
// parameters are references, so they arent copied
string make_plural(size_t, const string&, const string&);
这个函数隐式应用 string 复制机关函数返回给定单词的复数情势。形参是 const 引用,不克不及复制。
初始化容器元素
复制机关函数可用于初始化次序容器中的元素。
例如,可以用默示容量的单个形参来初始化容器。
容器的这种机关体式格式应用默认机关函数和复制机关函数:
// default string constructor and five string copy constructors invoked
vector<string> svec(5);
编译器起首应用 string 默认机关函数创建一个姑且值来初始化 svec,
然后应用复制机关函数将姑且值复制到 svec 的每个元素。
作为一般规矩,除非你想应用容器元素的默认初始值,
更有效的办法是,分派一个空容器并将已知元素的值参加容器。
机关函数与数组元素
若是没有为类类型数组供给元素初始化式,则将用默认机关函数初始化每个元素。
然而,若是应用常规的花括号括住的数组初始化列表来供给显式元素初始化式,则应用复制初始化来初始化每个元素。
按照指定值创建恰当类型的元素,然后用复制机关函数将该值复制到响应元素:
Sales_item primer_eds[] = { string(0-201-16487-6),
string(0-201-54848-8),
string(0-201-82470-1),
Sales_item()
};
如前三个元素的初始化式中所示可以直接指定一个值,用于调用元素类型的单实参机关函数。
若是不指定实参或指定多个实参,就须要应用完全的机关函数语法,正如最后一个元素的初始化那样。
1. 合成的复制机关函数
若是我们没有定义复制机关函数,编译器就会为我们合成一个。
与合成的默认机关函数不合,即使我们定义了其他机关函数,也汇合成复制机关函数。
合成复制机关函数的行动是,履行逐个成员初始化,将新对象初始化为原对象的副本。
所谓“逐个成员”,指的是编译器将如今对象的每个非 static 成员,依次复制到正创建的对象。
只有一个例外,每个成员类型决意了复制该成员的含义。
合成复制机关函数直接复制内置类型成员的值,类类型成员应用该类的复制机关函数进行复制。
数构成员的复制是个例外。
固然一般不克不及复制数组,但若是一个类具稀有构成员,则合成复制机关函数将复制数组。
复制数组时合成复制机关函数将复制数组的每一个元素。
逐个成员初始化最简单的概念模型是,将合成复制机关函数看作如许一个机关函数:
此中每个数据成员在机关函数初始化列表中进行初始化。
例如,对于我们的 Sales_item 类,它有三个数据成员:
class Sales_item {
// other members and constructors as before
private:
std::string isbn;
int units_sold;
double revenue;
};
// 合成复制机关函数如下所示:
Sales_item::Sales_item(const Sales_item &orig):
isbn(orig.isbn), // uses string copy constructor
units_sold(orig.units_sold), // copies orig.units_sold
revenue(orig.revenue) // copy orig.revenue
{ } // empty body
2. 定义本身的复制机关函数
复制机关函数就是接管单个类类型引用形参(凡是用 const 润饰)的机关函数:
class Foo {
public:
Foo(); // default constructor
Foo(const Foo&); // copy constructor
// ...
};
固然也可以定义接管非 const 引用的复制机关函数,但形参凡是是一个 const 引用。
因为用于向函数传递对象和从函数返回对象,该机关函数一般不该设置为 explicit。
复制机关函数应将实参的成员复制到正在机关的对象。
对很多类而言,合成复制机关函数只完成须要的工作。
只包含类类型成员或内置类型(但不是指针类型)成员的类,无须显式地定义复制机关函数,也可以复制。
然而,有些类必须对复制对象时产生的工作加以把握。
如许的类经常有一个数据成员是指针,或者有成员默示在机关函数平分派的其他资料。
而另一些类在创建新对象时必须做一些特定工作。这两种景象下,都必须定义复制机关函数。
凡是,定义复制机关函数最艰苦的项目组在于熟悉到须要复制机关函数。
只要能熟悉到须要复制机关函数,定义机关函数一般很是简单。
复制机关函数的定义与其他机关函数一样:它与类同名,没有返回值,
可以(并且应当)应用机关函数初始化列表初始化新创建对象的成员,可以在函数体中做任何其他须要工作。
后续章节中将给出一些须要定义复制机关函数的类的例子。
3. 禁止复制
有些类须要完全禁止复制。例如,iostream 类就不容许复制。
若是想要禁止复制,似乎可以省略复制机关函数,然而,若是不定义复制机关函数,编译器将合成一个。
为了防止复制,类必须显式声明其复制机关函数为 private。
若是复制机关函数是私有的,将不容许用户代码复制该类类型的对象,编译器将拒绝任何进行复制的测验测验。
然而,类的友元和成员仍可以进行复制。
若是想要连友元和成员中的复制也禁止,就可以声明一个(private)复制机关函数但不合错误其定义。
声明而不定义成员函数是合法的,然则,应用不决义成员的任何测验测验将导致链接失败。
经由过程声明(但不定义)private 复制机关函数,可以禁止任何复制类类型对象的测验测验:
用户代码中复制测验测验将在编译时标识表记标帜为错误,而成员函数和友元中的复制测验测验将在链接时导致错误。
不定义复制机关函数和/或默认机关函数,会严重局限类的应用。
不容许复制的类对象只能作为引用传递给函数或从函数返回,它们也不克不及用作容器的元素。
一般来说,好显式或隐式定义默认机关函数和复制机关函数。
只有不存在其他机关函数时才合成默认机关函数。
若是定义了复制机关函数,也必须定义默认机关函数。
文艺不是炫耀,不是花哨空洞的文字堆砌,不是一张又一张的逆光照片,不是将旅行的意义转化为名牌包和明信片的物质展示;很多时候它甚至完全不美——它嘶吼、扭曲,它会痛苦地抽搐,它常常无言地沉默。——艾小柯《文艺是一种信仰》
0. 复制机关函数
只有单个形参,并且该形参是对本类类型对象的引用(常用 const 润饰),如许的机关函数称为复制机关函数。
与默认机关函数一样,复制机关函数可由编译器隐式调用。
复制机关函数可用于:
按照另一个同类型的对象显式或隐式初始化一个对象。
复制一个对象,将它作为实参传给一个函数。
从函数返回时复制一个对象。
初始化次序容器中的元素。
按照元素初始化式列表初始化数组元素。
对象的定义情势
回想一下,C++ 支撑两种初始化情势:
直接初始化和复制初始化。复制初始化应用 = 符号,而直接初始化将初始化式放在圆括号中。
当用于类类型对象时,初始化的复制情势和直接情势有所不合:
直接初始化直接调用与实参匹配的机关函数,复制初始化老是调用复制机关函数。
复制初始化起首应用指定机关函数创建一个姑且对象,然后用复制机关函数将那个姑且对象复制到正在创建的对象:
string null_book = 9-999-99999-9; // copy-initialization
string dots(10, .); // direct-initialization
string empty_copy = string(); // copy-initialization
string empty_direct; // direct-initialization
对于类类型对象,只有指定单个实参或显式创建一个姑且对象用于复制时,才应用复制初始化。
创建 dots 时,调用参数为一个数量和一个字符的 string 机关函数并直接初始化 dots 的成员。
创建 null_book 时,编译器起首调用接管一个 C 风格字符串形参的 string 机关函数,创建一个姑且对象,
然后,编译器应用 string 复制机关函数将 null_book 初始化为那个姑且对象的副本。
empty_copy 和 empty_direct 的初始化都调用默认机关函数。
对前者初始化时,默认机关函数函数创建一个姑且对象,然后复制机关函数用该对象初始化 empty_copy。
对后者初始化时,直接运行 empty_direct 的默认机关函数。
支撑初始化的复制情势主如果为了与 C 的用法兼容。
当景象容许时,可以容许编译器跳过复制机关函数直接创建对象,但编译器没有任务如许做。
凡是直接初始化和复制初始化仅在初级别上存在差别。
然而,对于不支撑复制的类型,或者应用非 explicit 机关函数的时辰,它们有本质差别:
ifstream file1(filename); // ok: direct initialization
ifstream file2 = filename; // error: copy constructor is private
// This initialization is okay only if
// the Sales_item(const string&) constructor is not explicit
Sales_item item = string(9-999-99999-9);
ifstream 类定义了一个可用 C 风格字符串调用的机关函数,应用该机关函数初始化 file1。
看上去等效的 file2 初始化应用复制初始化,但该定义不正确。
因为不克不及复制 IO 类型的对象,所以不克不及对那些类型的对象应用复制初始化。
item 的初始化是否正确,取决于正在应用哪个版本的 Sales_item 类。
某些版本将参数为一个 string 的机关函数定义为 explicit。
若是机关函数是显式的,则初始化失败;若是机关函数不是显式的,则初始化成功。
形参与返回值
正如我们所知,当形参为非引用类型的时辰,将复制实参的值。
类似地,以非引用类型作返回值时,将返回 return 语句 中的值的副本。
当形参或返回值为类类型时,由复制机关函数进行复制。例如,推敲之前写过的 make_plural 函数:
// copy constructor used to copy the return value;
// parameters are references, so they arent copied
string make_plural(size_t, const string&, const string&);
这个函数隐式应用 string 复制机关函数返回给定单词的复数情势。形参是 const 引用,不克不及复制。
初始化容器元素
复制机关函数可用于初始化次序容器中的元素。
例如,可以用默示容量的单个形参来初始化容器。
容器的这种机关体式格式应用默认机关函数和复制机关函数:
// default string constructor and five string copy constructors invoked
vector<string> svec(5);
编译器起首应用 string 默认机关函数创建一个姑且值来初始化 svec,
然后应用复制机关函数将姑且值复制到 svec 的每个元素。
作为一般规矩,除非你想应用容器元素的默认初始值,
更有效的办法是,分派一个空容器并将已知元素的值参加容器。
机关函数与数组元素
若是没有为类类型数组供给元素初始化式,则将用默认机关函数初始化每个元素。
然而,若是应用常规的花括号括住的数组初始化列表来供给显式元素初始化式,则应用复制初始化来初始化每个元素。
按照指定值创建恰当类型的元素,然后用复制机关函数将该值复制到响应元素:
Sales_item primer_eds[] = { string(0-201-16487-6),
string(0-201-54848-8),
string(0-201-82470-1),
Sales_item()
};
如前三个元素的初始化式中所示可以直接指定一个值,用于调用元素类型的单实参机关函数。
若是不指定实参或指定多个实参,就须要应用完全的机关函数语法,正如最后一个元素的初始化那样。
1. 合成的复制机关函数
若是我们没有定义复制机关函数,编译器就会为我们合成一个。
与合成的默认机关函数不合,即使我们定义了其他机关函数,也汇合成复制机关函数。
合成复制机关函数的行动是,履行逐个成员初始化,将新对象初始化为原对象的副本。
所谓“逐个成员”,指的是编译器将如今对象的每个非 static 成员,依次复制到正创建的对象。
只有一个例外,每个成员类型决意了复制该成员的含义。
合成复制机关函数直接复制内置类型成员的值,类类型成员应用该类的复制机关函数进行复制。
数构成员的复制是个例外。
固然一般不克不及复制数组,但若是一个类具稀有构成员,则合成复制机关函数将复制数组。
复制数组时合成复制机关函数将复制数组的每一个元素。
逐个成员初始化最简单的概念模型是,将合成复制机关函数看作如许一个机关函数:
此中每个数据成员在机关函数初始化列表中进行初始化。
例如,对于我们的 Sales_item 类,它有三个数据成员:
class Sales_item {
// other members and constructors as before
private:
std::string isbn;
int units_sold;
double revenue;
};
// 合成复制机关函数如下所示:
Sales_item::Sales_item(const Sales_item &orig):
isbn(orig.isbn), // uses string copy constructor
units_sold(orig.units_sold), // copies orig.units_sold
revenue(orig.revenue) // copy orig.revenue
{ } // empty body
2. 定义本身的复制机关函数
复制机关函数就是接管单个类类型引用形参(凡是用 const 润饰)的机关函数:
class Foo {
public:
Foo(); // default constructor
Foo(const Foo&); // copy constructor
// ...
};
固然也可以定义接管非 const 引用的复制机关函数,但形参凡是是一个 const 引用。
因为用于向函数传递对象和从函数返回对象,该机关函数一般不该设置为 explicit。
复制机关函数应将实参的成员复制到正在机关的对象。
对很多类而言,合成复制机关函数只完成须要的工作。
只包含类类型成员或内置类型(但不是指针类型)成员的类,无须显式地定义复制机关函数,也可以复制。
然而,有些类必须对复制对象时产生的工作加以把握。
如许的类经常有一个数据成员是指针,或者有成员默示在机关函数平分派的其他资料。
而另一些类在创建新对象时必须做一些特定工作。这两种景象下,都必须定义复制机关函数。
凡是,定义复制机关函数最艰苦的项目组在于熟悉到须要复制机关函数。
只要能熟悉到须要复制机关函数,定义机关函数一般很是简单。
复制机关函数的定义与其他机关函数一样:它与类同名,没有返回值,
可以(并且应当)应用机关函数初始化列表初始化新创建对象的成员,可以在函数体中做任何其他须要工作。
后续章节中将给出一些须要定义复制机关函数的类的例子。
3. 禁止复制
有些类须要完全禁止复制。例如,iostream 类就不容许复制。
若是想要禁止复制,似乎可以省略复制机关函数,然而,若是不定义复制机关函数,编译器将合成一个。
为了防止复制,类必须显式声明其复制机关函数为 private。
若是复制机关函数是私有的,将不容许用户代码复制该类类型的对象,编译器将拒绝任何进行复制的测验测验。
然而,类的友元和成员仍可以进行复制。
若是想要连友元和成员中的复制也禁止,就可以声明一个(private)复制机关函数但不合错误其定义。
声明而不定义成员函数是合法的,然则,应用不决义成员的任何测验测验将导致链接失败。
经由过程声明(但不定义)private 复制机关函数,可以禁止任何复制类类型对象的测验测验:
用户代码中复制测验测验将在编译时标识表记标帜为错误,而成员函数和友元中的复制测验测验将在链接时导致错误。
不定义复制机关函数和/或默认机关函数,会严重局限类的应用。
不容许复制的类对象只能作为引用传递给函数或从函数返回,它们也不克不及用作容器的元素。
一般来说,好显式或隐式定义默认机关函数和复制机关函数。
只有不存在其他机关函数时才合成默认机关函数。
若是定义了复制机关函数,也必须定义默认机关函数。
文艺不是炫耀,不是花哨空洞的文字堆砌,不是一张又一张的逆光照片,不是将旅行的意义转化为名牌包和明信片的物质展示;很多时候它甚至完全不美——它嘶吼、扭曲,它会痛苦地抽搐,它常常无言地沉默。——艾小柯《文艺是一种信仰》