在自定义类中定义c 赋值运算符重载载函数时,通常将函数定义成按引用返回,而不是按值返回或void返回。请

在自定义类中定义赋值运算符重载函数时,通常将函数定义成按引用返回,而不是按值返回或void返回。请_百度知道
在自定义类中定义赋值运算符重载函数时,通常将函数定义成按引用返回,而不是按值返回或void返回。请
通常将函数定义成按引用返回在自定义类中定义赋值运算符重载函数时,而不是按值返回或void返回。请举例说明这样做的原因
提问者采纳
}定义成为引用,是为了避免返回后执行拷贝构造函数,const定义是为了符合=的语意,就像传引用参数也是为了避免拷贝构造函数,没有出现过 (v1=v2)=v3。因为应该返回右值(不能被改变)标准定义应该是 const T1& T1::operator=(T2){
return *this
提问者评价
太给力了,你的回答完美地解决了我的问题,非常感谢!
其他类似问题
为您推荐:
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁C++中运算符重载 - 攻城狮凌风 - 博客园
概述:运算符重载是C++多态的重要实现手段之一。一般而言,对于双目运算符,最好将其重载为友元函数;而对于单目运算符,则最好重载为成员函数。但是一定要记得其中的例外情况。
运算符重载的四项基本原则:
a.不可臆造运算符。
b.运算符原有操作数的个数、优先级和结合性不能改变。
c.操作数中至少一个是自定义类型。
d.保持重载运算符的自然含义。
将运算符重载为成员函数还是友元函数呢?先来看看两者的区别:
a.当重载为成员函数时,会隐含一个this指针;当重载为友元函数时,将不存在隐含的this指针,需要在参数列表中显示地添加操作数。
b.当重载为成员函数时,只允许右参数的隐式转换;当重载为友元函数时,能够接受左参数和右参数的隐式转换。如下代码:
class CString
&&&&CString(char* str);
&&&&char* m_pS
因为CString的构造函数参数为一个char*,所以如果采用友元形式的operator +(const CString&, const CString&),那么char+CString和CString+char都能正常工作;而如果采用的是成员函数形式的CString::operator +(const CString& rhs),则只能接受CString+char,如果执行char+CString则会编译出错。
需要注意的是,隐式转换由于临时变量的增加往往效率不高。如果应用程序对效率要求较高,针对以上类,建议选择定义多个运算符的友元重载版本:
CString& operator +(const CString&, const CString&);
CString& operator +(const char*, const CString&);
CString& operator +(const CString&, const char*);
一般来说,建议遵守这么一个不成文的规定:
对于双目运算符,最好将其重载为友元函数,因为这样更方便些;对于单目运算符,则最好重载为成员函数。
当然也有例外的情况,有些双目运算符是不能重载为友元函数的,比如赋值运算符=、函数调用运算符()、下表运算符[]、指针-&等,因为这些运算符在语义上与this都有太多的关联。
还有一个需要特别说明的就是输出运算符&&。因为&&的第一个操作数一定是ostream类型,所以&&只能重载为友元函数,如下:
friend ostream& operator &&(ostream& os, const Complex& c);
ostream& operator &&(ostream& os, const Complex& c)
&&&&os && c.m_Real && “+” && c.m_Imag && “i” &&
1、运算符重载是为了对用户自定义数据类型的数据的操作与内定义的数据类型的数据的操作形式一致。不能重载的5个运算符:*成员指针访问运算符;::域运算符;sizeof长度运算符;?:条件运算符;.成员访问符。
运算重载的三种方式:普通函数,友元函数,类成员函数。
当重载为成员函数时,双目运算符仅有一个参数。对单目运算符,重载为成员函数时,不能再显式说明参数。重载为成员函数时,总时隐含了一个参数,该参数是this指针。this指针是指向调用该成员函数对象的指针。&
运算符重载函数还可以为友元函数。当重载友元函数时,将没有隐含的参数this指针。这样,对双目运算符,友元函数有2个参数,对单目运算符,友元函数有一个参数。但是,有些运行符不能重载为友元函数,它们是:=,(),[]和->。
原因:有人说是因为
C++规定赋值运算符“=”只能重载为类的非静态成员函数,而不可以重载为类的友元函数。
不能重载为类的静态成员应该比较容易理解,因为静态成员函数是属于整个类的,不是属于某个对象的,它只能去操作类静态数据成员。而赋值运算符“=”是基于对象操作的。
当把赋值运算符重载为类的友员函数,在程序中执行类对象的赋值语句时,程序就会出现两种矛盾的选择。
(1)因为它认为类中并没有重载赋值运算符的成员函数,所以它根据C++的规则,会去调用相应的构造函数。
(2)但是在全局里,我们已经重载了参数类型为此类类型的赋值运算符函数,而这赋值语句刚好和这函数匹配上了,根据C++的规则,也会去调用这函数。
程序是不允许有矛盾不确定选择的,所以当赋值运算符重载为类的友元函数时,编译器就会提示错误。
2、流运算符为什么不能重载为成员函数,只能用友元函数重载,cout&&obj;
cout && f1 && f2;
&//用重载运算符表示,只能通过友员来实现,如果要用成员函数,则会有cout.operator&&(const F& f),所以这是不可能的.因此只能用友员来实现,operator&&(cout,f) 而cout是ostream型的,因此有以下标准格式.注意不能加const,因为cout是要改变的,会改变里的缓冲成员.
friend ostream& operator&&( ostream& cout, constF&)& //输出运算符的标准重载格式.
friend istream& operator&&(istream& is, F& f){ }&& //输入运算符重载标准格式
#include &iostream&&&
class F{&&
public :&&
&&&&&&& F(int n=0, int d=1):n(n),d(d){}&&
&&&&&&& friend ostream& operator&&(ostream& os, const F& f){&&
&&&&&&&&&&&&&&& os && '[' &&& f.n && '/' && f.d &&']';&&
&&&&&&&&&&&&&&&&&
&&&&&&& }&&
&&&&&&& F operator*(const F& o) {&&
&&&&&&&&&&&&&&& return F(n*o.n,d*o.d);&&
&&&&&&& }&&
&&&&&&& friend F operator/(const F& f1,const F& f2){&&
&&&&&&&&&&&&&&& return F(f1.n/f2.d,f1.d/f2.n);&&
&&&&&&& }&&
&&&&&&& friend istream& operator&&(istream& is, F& f){&&
&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&& is && f.n && ch && f.d;&&
&&&&&&&&&&&&&&&&&
&&&&&&& }&&
int main()& &
&&&&&&& F f1(3,5),f2(11,7),f;&&
&&&&&&& cout && f1 && '*' && f2 && '=' && f1*f2 &&&&
&&&&&&& cout && f1 && '/' && f2 && '=' && f1/f2 &&&&
&&&&&&& cout && &Input 2 fractions :&;&&
&&&&&&& cin && f1 && f2;&&
&&&&&&& cout &&&f1=& && f1 &&&&
&&&&&&& cout && &f2=& && f2 &&&&
3、在类成员函数中重载运算符是不允许返回引用的,会出现“返回局部变量的地址”警告
4、把后++,后--当作双目运算符,第二个操作数是整形
单目运算符重载
-友元函数形式:返回类型 operatorX(形参)
使用:X obj ---& operatorX(obj);
-成员函数形式 尽量用成员:返回类型 operatorX(/*无形参*/)
使用: X obj ---& obj.operator();
#include &iostream&&&
class A{&&
public :&&
&&&&&&& A(int d=0):data(d){}&&
&&&&&&& friend ostream& operator&&(ostream& os,const A& a){&&
&&&&&&& os && a.&&
&&&& &&&}&&
&&&&&&& friend istream& operator&&(istream& is,A& a){&&
&&&&&&& is && a.&&
&&&&&&& }&&
&&&&&&& friend A& operator++(A& a){&&
&&&&&&&&&&&&&&& a.data += 10;&&
&&&&&&&&&&&&&&&&&
&&&&&&& }&&
&&&&&&& A& operator--(){&&
&&&&&&&&&&&&&&& data -= 10;&&
&&&&&&&&&&&&&&& return *&&
&&&&&&& }&&
&&&&&&& friend A/* 不能用引用 */ operator++(A& a,int){&&
&&&&&&&&&&&&&&& A old(a);&&
&&&&&&&&&&&&&&& a.data += 1;&&
&&&&&&&&&&&&&&&&&
&&&&&&& }&&
&&&&&&& A /* 不能用引用 */ operator--(int){&&
&&&&&&&&&&&&&&& A old(*this);&&
&&&&&&&&&&&&&&& data -= 1;&&
&&&&&&&&&&&&&&&&&
&&&&&&& }&&
int main()&&
&&&&&&& A a1(50),a2(100);&&
&&&&&&& cout && &a1=& &&a1 &&&&
&&&&&&& cout && &a2=& &&a2 &&&&
&&&&&&& cout && &++a1=& && ++a1 &&&&
&&&&&&& cout && &--a1=& && --a1 &&&&
&&&&&&& cout && &a1++=& && a1++ &&&&
&&&&&&& cout && &a1=& &&a1 &&&&
&&&&&&& cout && &a2--=& && a2-- &&&&
&&&&&&& cout && &a2=& &&a2 &&&&
5、强制类型转换:类型(数据) --& (不必写返回类型,因为始终与后面的类 型是相同的) operator类型(无形参) 只能写成成员函数,不能是友员.
#include&iostream&&
A(intd=0):data(d){}&
operator int(){&
operator bool(){&
returndata!=0;&
operator char(){&
return(char)&
intmain()&
A a1(65),a2(200);&
cout && &a1=&&& (char)a1 &&&
cout && &good&&&&
随笔 - 216c++中为什么赋值运算符重载返回类型是引用_百度知道
c++中为什么赋值运算符重载返回类型是引用
  一、c/c++赋值运算符的本意为“返回左值的引用”(左值:赋值号左面的变量而非其值)  例:  int a, b = 3, c = 2;
cout&&a&&  对于a = b(a,b均为对象时),若不返回左值的引用,将会生成临时对象。如果不处理a = b = c这样的表达式,也会正常(只是会调用拷贝构造函数和析构函数处理临时对象)。  二、为了进行连续赋值,即 x = y = z  1、赋值返回引用  x = y = z
先执行y = z,返回y的引用,执行x = y  2、赋值不返回引用  x = y = z
先执行y = z,返回用y初始化的临时对象(注意临时对象都是常对象),再执行x = y的临时对象(要求operator=(const X&)  ),返回用x初始化的临时对象(此处要求拷贝构造函数必须为X(const X&)
)。  所以也并非必须返回引用,返回引用的好处既可以于赋值的原始语义已知,又可避免拷贝构造函数和析构函数的调用。
其他类似问题
为您推荐:
提问者采纳
当然,你若返回引用大多数情况下也不会出错或导致某个操作数被意外修改,而 + 之类的运算符不会改变操作数,这就有点不符合约定了,但这就使(a+b)=c这样的表达式可以出现因为赋值操作会改变左值。+ 返回一个临时对象是合情合理的 ,所以说赋值运算符重载要返回引用以用于类似 (a=b)=c 这样的再次对a=b进行写操作的表达式,你也可以让 + 返回一个常引用
其他2条回答
这只是个协议,适用于所有赋值相关运算符,所有内置类型和标准库类型都这样遵循,具体可参考Effective C++ 第三版 条款10
令 operator= 返回一个 reference to *this
不要一定非要是引用的,用引用是为了可以写A+B+C这样的连续表达式返回的不是引用或指针则只能用于赋值
赋值运算符的相关知识
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁C++类(Class)总结
一、C++类的定义
&& &C++中使用关键字&class&来定义类, 其基本形式如下:class 类名{
//公共的行为或属性&
//公共的行为或属性
示例:& & &定义一个点(Point)类, 具有以下属性和方法:& & &■ 属性: x坐标, y坐标& & &■ 方法: 1.设置x,y的坐标值; 2.输出坐标的信息。实现代码:
class Point
& & &void setPoint(int x, int y);
& & &void printPoint();
& & &int xP
& & &int yP
代码说明:& & &上段代码中定义了一个名为 Point 的类, 具有两个私密属性, int型的xPos和yPos, 分别用来表示x点和y点。
& & &在方法上,&setPoint&用来设置属性, 也就是 xPos 和 yPos 的值;&printPoint&用来输出点的信息。& &&
1 数据抽象和封装
& & &抽象是通过特定的实例抽取共同特征以后形成概念的过程。一个对象是现实世界中一个实体的抽象,一个类是一组对象的抽象。& & &封装是将相关的概念组成一个单元,然后通过一个名称来引用它。面向对象封装是将数据和基于数据的操作封装成一个整体对象,对数据的访问或修改只能通过对象对外提供的接口进行。
& & &几个重要名词:
& & &遵循一般的命名规则; 字母,数字和下划线组合,不要以数字开头。
(2) 类成员
& & &类可以没有成员,也可以定义多个成员。成员可以是数据、函数或类型别名。所有的成员都必须在类的内部声明。
& & &没有成员的类是空类,空类也占用空间。
class&People
sizeof(People) = 1;& &&
(3) 构造函数
& & &构造函数是一个特殊的、与类同名的成员函数,用于给每个数据成员设置适当的初始值。
(4) 成员函数
& & &成员函数必须在类内部声明,可以在类内部定义,也可以在类外部定义。如果在类内部定义,就默认是内联函数。
3 类定义补充
3.1 可使用类型别名来简化类
& & &除了定义数据和函数成员之外,类还可以定义自己的局部类型名字。
& & &使用类型别名有很多好处,它让复杂的类型名字变得简单明了、易于理解和使用,还有助于程序员清楚地知道使用该类型的真实目的。
class&People
& & &typedef std::string&phonenum;&//电话号码类型
& & &phonenum&phoneP //公开号码
private:&& & &
& & &phonenum&phoneP//私人号码
3.2&成员函数可被重载
& & &可以有多个重载成员函数,个数不限。
3.3&内联函数
& & &有三种:
(1)直接在类内部定义。
(2)在类内部声明,加上inline关键字,在类外部定义。
(3)在类内部声明,在类外部定义,同时加上inline关键字。注意:此种情况下,内联函数的定义通常应该放在类定义的同一头文件中,而不是在源文件中。这是为了保证内联函数的定义在调用该函数的每个源文件中是可见的。
3.4 访问限制
& & &public,private,protected&为属性/方法限制的关键字。
3.5 类的数据成员中不能使用 auto、extern和register等进行修饰, 也不能在定义时进行初始化
& & &如&int xPos = 0;&//错;
例外:& & & & & 静态常量整型(包括char,bool)数据成员可以直接在类的定义体中进行初始化,例如:
& & & & & static const int ia= 30;&
4 类声明与类定义
4.1 类声明(declare)class&Screen;
& & & 在声明之后,定义之前,只知道Screen是一个类名,但不知道包含哪些成员。只能以有限方式使用它,不能定义该类型的对象,只能用于定义指向该类型的指针或引用,声明(不是定义)使用该类型作为形参类型或返回类型的函数。void Test1(Screen& a){};
void Test1(Screen* a){};
4.2 类定义(define)
& & &在创建类的对象之前,必须完整的定义该类,而不只是声明类。所以,类不能具有自身类型的数据成员,但可以包含指向本类的指针或引用。class&LinkScreen
& & & & &&Screen&
& & & & &&LinkScreen*
& & & & &&LinkScreen*
};&//注意,分号不能丢
& & &因为在类定义之后可以接一个对象定义列表,可类比内置类型,定义必须以分号结束:class&LinkScreen{ /* ... */ };
class&LinkScreen{ /* ... */ } scr1,scr2;&
& & &定义类对象时,将为其分配存储空间。
& & &Sales_&//编译器分配了足以容纳一个 Sales_item 对象的存储空间。item 指的就是那个存储空间。
6 隐含的 this 指针&
& & &成员函数具有一个附加的隐含形参,即&this指针,它由编译器隐含地定义。成员函数的函数体可以显式使用 this 指针。
6.1 何时使用 this 指针
& & &当我们需要将一个对象作为整体引用而不是引用对象的一个成员时。最常见的情况是在这样的函数中使用 this:该函数返回对调用该函数的对象的引用。
class Screen&
& & & Screen& set(char);
Screen& Screen::set(char c)&
& & & contents[cursor] =
& & & return *this;
7&类作用域
& & &每个类都定义了自己的作用域和唯一的类型。
& & &类的作用域包括:类的内部(花括号之内), 定义在类外部的成员函数的参数表(小括号之内)和函数体(花括号之内)。
class Screen&
//类的内部
//类的外部
char Screen::get(index r, index c) const
& & &index row = r *&&&&& // compute the row location
& & &return contents[row + c];&& // offset by c to fetch specified&character
& & &注意:成员函数的返回类型不一定在类作用域中。可通过 类名::来判断是否是类的作用域,::之前不属于类的作用域,::之后属于类的作用域。例如
Screen:: 之前的返回类型就不在类的作用域,Screen:: 之后的函数名开始到函数体都是类的作用域。
class Screen&
& & &typedef std::string::size_&
& & &index&get_cursor()&
Screen::index&Screen::get_cursor() const & //注意:index前面的Screen不能少
& & &该函数的返回类型是 index,这是在 Screen 类内部定义的一个类型名。在类作用域之外使用,必须用完全限定的类型名 Screen::index 来指定所需要的 index 是在类 Screen 中定义的名字。
二 构造函数
& & &构造函数是特殊的成员函数,用来保证每个对象的数据成员具有合适的初始值。
& & &构造函数名字与类名相同,不能指定返回类型(也不能定义返回类型为void),可以有0-n个形参。
& & &在创建类的对象时,编译器就运行一个构造函数。
1 构造函数可以重载
& & &可以为一个类声明的构造函数的数量没有限制,只要每个构造函数的形参表是唯一的。
class Sales_
& & &Sales_item(const std::string&);&
& & &Sales_item(std::istream&);&
& & &Sales_item(); //默认构造函数
2 构造函数自动执行&
& & &只要创建该类型的一个对象,编译器就运行一个构造函数:Sales_item item1("0-201-54848-8");
Sales_item *p = new Sales_item();&
& & &第一种情况下,运行接受一个 string 实参的构造函数,来初始化变量item1。
& & &第二种情况下,动态分配一个新的 Sales_item 对象,通过运行默认构造函数初始化该对象。
3 构造函数初始化式
& & &与其他函数一样,构造函数具有名字、形参表和函数体。
& & &与其他函数不同的是,构造函数可以包含一个构造函数初始化列表:&&
Sales_item::Sales_item(const string &book):&isbn(book), units_sold(0), revenue(0.0)
& & &构造函数初始化列表以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个数据成员后面跟一个放在圆括号中的初始化式。
& & &构造函数可以定义在类的内部或外部。构造函数初始化只在构造函数的定义中指定。
& & &构造函数分两个阶段执行:(1)初始化阶段;(2)普通的计算阶段。初始化列表属于初始化阶段(1),构造函数函数体中的所有语句属于计算阶段(2)。& & &初始化列表比构造函数体先执行。不管成员是否在构造函数初始化列表中显式初始化,类类型的数据成员总是在初始化阶段初始化。
3.1 哪种类需要初始化式
& & &const 对象或引用类型的对象,可以初始化,但不能对它们赋值,而且在开始执行构造函数的函数体之前要完成初始化。
& & &初始化 const 或引用类型数据成员的唯一机会是构造函数初始化列表中,在构造函数函数体中对它们赋值不起作用。& & &没有默认构造函数的类类型的成员,以及 const 或引用类型的成员,必须在初始化列表中完成初始化。
class ConstRef&
& & &ConstRef(int ii);&
& & &const&&
& & &int &&
ConstRef::ConstRef(int ii)&
& & &i = & // ok&
& & &ci = &// error
& & &ri = & //&
& & &应该这么初始化:
ConstRef::ConstRef(int ii): i(ii), ci(i), ri(ii) { }&
3.2 成员初始化的次序
& & &每个成员在构造函数初始化列表中只能指定一次。重复初始化,编译器一般会有提示。& & &成员被初始化的次序就是定义成员的次序,跟初始化列表中的顺序无关。3.3 初始化式表达式
& & &初始化式可以是任意表达式Sales_item(const std::string &book, int cnt, double&price): isbn(book), units_sold(cnt), revenue(cnt * price) { }3.4 类类型的数据成员的初始化式
& & &初始化类类型的成员时,要指定实参并传递给成员类型的一个构造函数,可以使用该类型的任意构造函数。
Sales_item(): isbn(10, '9'), units_sold(0), revenue(0.0) {}
&3.5&类对象的数据成员的初始化&& &&&
& & &在类A的构造函数初始化列表中没有显式提及的每个成员,使用与初始化变量相同的规则来进行初始化。
& & &类类型的数据成员,运行该类型的默认构造函数来初始化。
& & &内置或复合类型的成员的初始值依赖于该类对象的作用域:在局部作用域中不被初始化,在全局作用域中被初始化为0。假设有一个类A,class A
& & public:
& & A类对象A不管a在局部作用域还是全局作用域,b使用B类的默认构造函数来初始化,ia的初始化取决于a的作用域,a在局部作用域,ia不被初始化,a在全局作用域,ia初始化0。
4 默认构造函数&
& & &不含形参的构造函数就是默认构造函数。 & &&
& & &只要定义一个对象时没有提供初始化式,就使用默认构造函数。如: A
& & &为所有形参提供默认实参的构造函数也定义了默认构造函数。例如:
& & &A(int a=1,char c =''){}
private: &
& & &char c1;
4.1 合成的默认构造函数
& & &只有当一个类没有定义构造函数时,编译器才会自动生成一个默认构造函数。
& & &一个类只要定义了一个构造函数,编译器也不会再生成默认构造函数。
& & &如果定义了其他构造函数,也提供一个默认构造函数。
& & &如果类包含内置或复合类型(如 int& 或 string*)的成员,它应该定义自己的构造函数来初始化这些成员。每个构造函数应该为每个内置或复合类型的成员提供初始化。
5 隐式类类型转换5.1&只含单个形参的构造函数能够实现从形参类型到该类类型的一个隐式转换
& & &A(int a)
& & & & & ia =a;
& & &bool EqualTo(const A& a)
& & & & & return ia == a.
bool bEq =
bEq =&a.EqualTo(1);//参数为1,实现从int型到A的隐式转换
5.2抑制由构造函数定义的隐式转换
& & &通过将构造函数声明为&explicit,来防止在需要隐式转换的上下文中使用构造函数:&
& & &explicit&A(int a )
& & & & & ia =a;
& & &bool EqualTo(const A& a)
& & & & & return ia == a.
& & &通常,除非有明显的理由想要定义隐式转换,否则,单形参构造函数应该为 explicit。将构造函数设置为 explicit 可以避免错误。
三 复制控制
1 复制构造函数1.1 几个要点
(1) 复制构造函数
& & &复制构造函数是一种特殊构造函数,只有1个形参,该形参(常用 const &修饰)是对该类类型的引用。
class Peopel
& & &Peopel();//默认构造函数
& & &Peopel(const Peopel&);//复制构造函数
& & &~Peopel();//析构函数
& & &当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式使用复制构造函数。
Peopel&a1;&Peopel&a2 = a1;
& & &当将该类型的对象传递给函数或函数返回该类型的对象时,将隐式使用复制构造函数。
Peopel&Func(Peopel&b){...}
(2)析构函数
& & &析构函数是构造函数的互补:当对象超出作用域或动态分配的对象被删除时,将自动应用析构函数。
& & &析构函数可用于释放构造对象时或在对象的生命期中所获取的资源。
& & &不管类是否定义了自己的析构函数,编译器都自动执行类中非 static 数据成员的析构函数。(3) 复制控制
& & &复制构造函数、赋值操作符和析构函数总称为复制控制。编译器自动实现这些操作,但类也可以定义自己的版本。
(4)&两种初始化形式
& & &C++ 支持两种初始化形式:直接初始化和复制初始化。直接初始化将初始化式放在圆括号中,复制初始化使用 = 符号。& & &对于内置类型,例如int, double等,直接初始化和复制初始化没有区别。
& & &对于类类型:直接初始化直接调用与实参匹配的构造函数;复制初始化先使用指定构造函数创建一个临时对象,然后用复制构造函数将那个临时对象复制到正在创建的对象。直接初始化比复制初始化更快。(5)形参和返回值
& & &当形参或返回值为类类型时,由该类的复制构造函数进行复制。&
(6)初始化容器元素
& & &复制构造函数可用于初始化顺序容器中的元素。例如:
vector&string& svec(5);
& & &编译器首先使用 string 默认构造函数创建一个临时值,然后使用复制构造函数将临时值复制到 svec 的每个元素。&(7)构造函数与数组元素
& & &如果没有为类类型数组提供元素初始化式,则将用默认构造函数初始化每个元素。
& & &如果使用常规的花括号括住的数组初始化列表来提供显式元素初始化式,则使用复制初始化来初始化每个元素。根据指定值创建适当类型的元素,然后用复制构造函数将该值复制到相应元素:Sales_item primer_eds[] = { string("0-201-16487-6"),
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& string("0-201-54848-8"),
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& string("0-201-82470-1"),
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& Sales_item()
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& };
1.2&合成的复制构造函数
(1)合成的复制构造函数
& & &如果没有定义复制构造函数,编译器就会为我们合成一个。
& & &合成复制构造函数的行为是,执行逐个成员初始化,将新对象初始化为原对象的副本。逐个成员初始化:合成复制构造函数直接复制内置类型成员的值,类类型成员使用该类的复制构造函数进行复制。例外:如果一个类具有数组成员,则合成复制构造函数将复制数组。复制数组时合成复制构造函数将复制数组的每一个元素。
1.3 定义自己的复制构造函数
(1) 只包含类类型成员或内置类型(但不是指针类型)成员的类,无须显式地定义复制构造函数,也可以复制。&
class Peopel
& & &std::
& & &std::
(2) 有些类必须对复制对象时发生的事情加以控制。
& & &例如,类有一个数据成员是指针,或者有成员表示在构造函数中分配的其他资源。而另一些类在创建新对象时必须做一些特定工作。这两种情况下,都必须定义自己的复制构造函数。
& & &最好显式或隐式定义默认构造函数和复制构造函数。如果定义了复制构造函数,必须定义默认构造函数。
1.4 禁止复制
& & &有些类需要完全禁止复制。例如,iostream 类就不允许复制。延伸:容器内元素不能为iostream&& & &为了防止复制,类必须显式声明其复制构造函数为 private。
2 赋值操作符& & &与复制构造函数一样,如果类没有定义自己的赋值操作符,则编译器会合成一个。
(1)重载赋值操作符
Sales_item&&operator=(const Sales_item &);(2)合成赋值操作符
& & &合成赋值操作符会逐个成员赋值:右操作数对象的每个成员赋值给左操作数对象的对应成员。除数组之外,每个成员用所属类型的常规方式进行赋值。对于数组,给每个数组元素赋值。
(3)复制和赋值常一起使用&
& & &一般而言,如果类需要复制构造函数,它也会需要赋值操作符。&
3 析构函数
& & &构造函数的用途之一是自动获取资源;与之相对的是,析构函数的用途之一是回收资源。除此之外,析构函数可以执行任意类设计者希望在该类对象的使用完毕之后执行的操作。(1) 何时调用析构函数
撤销(销毁)类对象时会自动调用析构函数。
变量(类对象)在超出作用域时应该自动撤销(销毁)。
动态分配的对象(new A)只有在指向该对象的指针被删除时才撤销(销毁)。
撤销(销毁)一个容器(不管是标准库容器还是内置数组)时,也会运行容器中的类类型元素的析构函数(容器中的元素总是从后往前撤销)。
(2)何时编写显式析构函数
& & &如果类需要定义析构函数,则它也需要定义赋值操作符和复制构造函数,这个规则常称为三法则:如果类需要析构函数,则需要所有这三个复制控制成员。(3)合成析构函数
& & &合成析构函数按对象创建时的逆序撤销每个非 static 成员,因此,它按成员在类中声明次序的逆序撤销成员。
& & &对于每个类类型的成员,合成析构函数调用该成员的析构函数来撤销对象。
& & &合成析构函数并不删除指针成员所指向的对象。 所以,如果有指针成员,一定要定义自己的析构函数来删除指针。
& & &析构函数与复制构造函数或赋值操作符之间的一个重要区别:即使我们编写了自己的析构函数,合成析构函数仍然运行。
& & &友元机制允许一个类将对其非公有成员的访问权授予指定的函数或类。
& & &友元可以出现在类定义的内部的任何地方。
& & &友元不是授予友元关系的那个类的成员,所以它们不受声明出现部分的访问控制影响。
& & &建议:将友元声明成组地放在类定义的开始或结尾。
class Husband
& & &friend&class W
& & &//钱是老公私有的,别人不能动,但老婆除外
class Wife
& & &void Consume(Husband& h)
& & & & & h.money -= 10000;//老婆可以花老公的钱
w.Consume(h);
2&使其他类的成员函数成为友元
class H&//1.声明Husband&
class Wife&//2.定义Wife类&
& & &void Consume(Husband& h);
class Husband&//3.定义Husband类
& & &friend void Wife::Consume(Husband& h);//声明Consume函数。
& & &//钱是老公私有的,别人不能动,但老婆除外
void Wife::Consume(Husband& h)&//4.定义Consume函数。
& & &h.money -= 10000;//老婆可以花老公的钱
注意类和函数的声明和定义的顺序:
(1)声明类Husband&
(2)定义类Wife,声明Consume函数
(3)定义类Husband
(4)定义Consume函数。
五 static 类成员
static 成员,有全局对象的作用,但又不破坏封装。
1 static 成员变量
static 数据成员是与类关联的对象,并不与该类的对象相关联。
static 成员遵循正常的公有/私有访问规则。&&
2 使用 static 成员而不是全局对象有三个优点。(1) &static 成员的名字是在类的作用域中,因此可以避免与其他类的成员或全局对象名字冲突。
(2) &可以实施封装。static 成员可以是私有成员,而全局对象不可以。
(3) &通过阅读程序容易看出 static 成员是与特定类关联的,这种可见性可清晰地显示程序员的意图。&
3 static 成员函数
& & &在类的内部声明函数时需要添加static关键字,但是在类外部定义函数时就不需要了。
& & &因为static 成员是类的组成部分但不是任何对象的组成部分,所以有以下几个特点:1) static 函数没有 this 指针
2) static 成员函数不能被声明为 const (将成员函数声明为 const 就是承诺不会修改该函数所属的对象)
3) static 成员函数也不能被声明为虚函数
4 static 数据成员&
& & &static 数据成员可以声明为任意类型,可以是常量、引用、数组、类类型,等等。
& & &static 数据成员必须在类定义体的外部定义(正好一次),并且应该在定义时进行初始化。
建议:定义在类的源文件中名,即与类的非内联函数的定义同一个文件中。注意,定义时也要带上类类型+"::"double Account::interestRate = 0.035;&
5 特殊的静态常量整型成员&
& & &静态常量整型数据成员可以直接在类的定义体中进行初始化,例如:
static const int period = 30;&
& & &当然char 可以转换成整形,也是可以的,&&&static&const char&bkground&= '#';
6 其他(1)static 数据成员的类型可以是该成员所属的类类型。非 static 成员只能是自身类对象的指针或引用&class&Screen&
&&&&&&&& // ...
& & & & &static&Screen src1; // ok
& & & & &Screen&*src2;&&&&&& // ok
& & & & &Screen&src3;&&&&&&& // error
};&(2)非 static 数据成员不能用作默认实参,static 数据成员可用作默认实参
class Screen&
& & & & & Screen& clear(char =&bkground);
& & & & &static&const char&bkground&= '#';//static const整形变量可以在类内部初始化。
阅读(...) 评论()

我要回帖

更多关于 c 赋值运算符重载 的文章

 

随机推荐