1.constructor构造函数
构造函数名称=类名。构造函数的作用:初始化对象的数据成员。
1.1 构造函数的种类
引自:
http://www.cnblogs.com/xkfz007/archive/2012/05/11/2496447.html
class Complex
{
private :
double m_real;
double m_imag;
public:
// 无参数构造函数
// 如果创建一个类你没有写任何构造函数,则系统会自动生成默认的无参构造函数,函数为空,什么都不做
// 只要你写了一个下面的某一种构造函数,系统就不会再自动生成这样一个默认的构造函数,如果希望有一个这样的无参构造函数,则需要自己显示地写出来
Complex(void)
{
m_real = 0.0;
m_imag = 0.0;
}
// 一般构造函数(也称重载构造函数)
// 一般构造函数可以有各种参数形式,一个类可以有多个一般构造函数,前提是参数的个数或者类型不同(基于c++的重载函数原理)
// 例如:你还可以写一个 Complex( int num)的构造函数出来
// 创建对象时根据传入的参数不同调用不同的构造函数
Complex(double real, double imag)
{
m_real = real;
m_imag = imag;
}
// 复制构造函数(也称为拷贝构造函数)
// 复制构造函数参数为类对象本身的引用,用于根据一个已存在的对象复制出一个新的该类的对象,一般在函数中会将已存在对象的数据成员的值复制一份到新创建的对象中
// 若没有显示的写复制构造函数,则系统会默认创建一个复制构造函数,但当类中有指针成员时,由系统默认创建该复制构造函数会存在风险,具体原因请查询 有关 “浅拷贝” 、“深拷贝”的文章论述
Complex(const Complex & c)
{
// 将对象c中的数据成员值复制过来
m_real = c.m_real;
m_imag = c.m_imag;
}
// 类型转换构造函数,根据一个指定的类型的对象创建一个本类的对象,
//需要注意的一点是,这个其实就是一般的构造函数,但是对于出现这种单参数的构造函数,C++会默认将参数对应的类型转换为该类类型,有时候这种隐私的转换是我们所不想要的,所以需要使用explicit来限制这种转换。
// 例如:下面将根据一个double类型的对象创建了一个Complex对象
Complex(double r)
{
m_real = r;
m_imag = 0.0;
}
// 等号运算符重载(也叫赋值构造函数)
// 注意,这个类似复制构造函数,将=右边的本类对象的值复制给等号左边的对象,它不属于构造函数,等号左右两边的对象必须已经被创建
// 若没有显示的写=运算符重载,则系统也会创建一个默认的=运算符重载,只做一些基本的拷贝工作
Complex &operator=( const Complex &rhs )
{
// 首先检测等号右边的是否就是左边的对象本身,若是本对象本身,则直接返回
if ( this == &rhs )
{
return *this;
}
// 复制等号右边的成员到左边的对象中
this->m_real = rhs.m_real;
this->m_imag = rhs.m_imag;
// 把等号左边的对象再次传出
// 目的是为了支持连等 eg: a=b=c 系统首先运行 b=c
// 然后运行 a= ( b=c的返回值,这里应该是复制c值后的b对象)
return *this;
}
};
下面使用上面定义的类对象来说明各个构造函数的用法:
int main()
{
//调用了无参构造函数,数据成员初值被赋为0.0
Complex c1,c2;
//调用一般构造函数,数据成员初值被赋为指定值
Complex c3(1.0,2.5);
//也可以使用下面的形式
Complex c3 = Complex(1.0,2.5);
//把c3的数据成员的值赋值给c1
//由于c1已经事先被创建,故此处不会调用任何构造函数
//只会调用 = 号运算符重载函数
c1 = c3;
//调用类型转换构造函数
//系统首先调用类型转换构造函数,将5.2创建为一个本类的临时对 象,然后调用等号运算符重载,将该临时对象赋值给c1
c2 = 5.2;
// 调用拷贝构造函数( 有下面两种调用方式)
Complex c5(c2);
Complex c4 = c2; // 注意和 = 运算符重载区分,这里等号左边的对象不是事先已经创建,故需要调用拷贝构造函数,参数为c2
//这一点特别重要,这儿是初始化,不是赋值。其实这儿就涉及了 C++中的两种初始化的方式:复制初始化和赋值初始化。其中c5采用的是复制初始化,而c4采用的是赋值初始化,这两种方式都是要调用拷贝构造函数的。
}
1.2随堂笔记
构造函数都有initialization list(初始化列表),写代码时应充分利用初始化列表,使用初始化列表比在函数体内初始化成员变量效率高。
函数可以overloading(重载),允许同名函数,但是实参不能一样,但是在编译后的名称实际是不一样的,不存在编译后名称一样的两个函数。函数重载常常发生在构造函数。
已有一个构造函数,实参均有默认值,则不能再写一个无参数构造函数。两者均可为默认构造函数,实际上有冲突。
constructor放在private区域: singleton 单例模式
2.C++运算符重载
2.1C++运算符重载
C++中预定义的运算符的操作对象只能是基本数据类型。但实际上,对于许多用户自定义类型(例如类),也需要类似的运算操作。这时就必须在C++中重新定义这些运算符,赋予已有运算符新的功能,使它能够用于特定类型执行特定的操作。运算符重载的实质是函数重载,它提供了C++的可扩展性,也是C++最吸引人的特性之一。
运算符重载是通过创建运算符函数实现的,运算符函数定义了重载的运算符将要进行的操作。运算符函数的定义与其他函数的定义类似,惟一的区别是运算符函数的函数名是由关键字operator和其后要重载的运算符符号构成的。运算符函数定义的一般格式如下:
<返回类型说明符>operator<运算符符号>(<参数表>)
{
<函数体>
}
运算符重载时要遵循以下规则:
- (1) 除了类属关系运算符"."、成员指针运算符".*"、作用域运算符"::"、sizeof运算符和三目运算符"?:"以外,C++中的所有运算符都可以重载。
- (2) 重载运算符限制在C++语言中已有的运算符范围内的允许重载的运算符之中,不能创建新的运算符。
- (3) 运算符重载实质上是函数重载,因此编译程序对运算符重载的选择,遵循函数重载的选择原则。
- (4) 重载之后的运算符不能改变运算符的优先级和结合性,也不能改变运算符操作数的个数及语法结构。
- (5) 运算符重载不能改变该运算符用于内部类型对象的含义。它只能和用户自定义类型的对象一起使用,或者用于用户自定义类型的对象和内部类型的对象混合使用时。
- (6) 运算符重载是针对新类型数据的实际需要对原有运算符进行的适当的改造,重载的功能应当与原有功能相类似,避免没有目的地使用重载运算符。
运算符函数重载一般有两种形式:重载为类的成员函数和重载为类的非成员函数。非成员函数通常是友元。(可以把一个运算符作为一个非成员、非友元函数重载。但是,这样的运算符函数访问类的私有和保护成员时,必须使用类的公有接口中提供的设置数据和读取数据的函数,调用这些函数时会降低性能。可以内联这些函数以提高性能。)
成员函数运算符
运算符重载为类的成员函数的一般格式为:
<函数类型>operator<运算符>(<参数表>)
{
<函数体>
}
当运算符重载为类的成员函数时,函数的参数个数比原来的操作数要少一个(后置单目运算符除外),这是因为成员函数用this指针隐式地访问了类的一个对象,它充当了运算符函数最左边的操作数。因此:
- (1) 双目运算符重载为类的成员函数时,函数只显式说明一个参数,该形参是运算符的右操作数。
- (2) 前置单目运算符重载为类的成员函数时,不需要显式说明参数,即函数没有形参。
- (3) 后置单目运算符重载为类的成员函数时,函数要带有一个整型形参。
调用成员函数运算符的格式如下:
<对象名>.operator<运算符>(<参数>)
它等价于
<对象名><运算符><参数>
例如:a+b等价于a.operator+(b)。一般情况下,我们采用运算符的习惯表达方式。
友元函数运算符
运算符重载为类的友元函数的一般格式为:
friend<函数类型>operator<运算符>(<参数表>)
{
<函数体>
}
当运算符重载为类的友元函数时,由于没有隐含的this指针,因此操作数的个数没有变化,所有的操作数都必须通过函数的形参进行传递,函数的参数与操作数自左至右一一对应。
调用友元函数运算符的格式如下:
operator<运算符>(<参数1>,<参数2>)
它等价于
<参数1><运算符><参数2>
例如:a+b等价于operator+(a,b)。
两种重载形式的比较
在多数情况下,将运算符重载为类的成员函数和类的友元函数都是可以的。但成员函数运算符与友元函数运算符也具有各自的一些特点:
- (1) 一般情况下,单目运算符最好重载为类的成员函数;双目运算符则最好重载为类的友元函数。
- (2) 以下一些双目运算符不能重载为类的友元函数:=、()、[]、->。
- (3) 类型转换函数只能定义为一个类的成员函数而不能定义为类的友元函数。
- (4) 若一个运算符的操作需要修改对象的状态,选择重载为成员函数较好。
- (5) 若运算符所需的操作数(尤其是第一个操作数)希望有隐式类型转换,则只能选用友元函数。
- (6) 当运算符函数是一个成员函数时,最左边的操作数(或者只有最左边的操作数)必须是运算符类的一 个类对象(或者是对该类对象的引用)。如果左边的操作数必须是一个不同类的对象,或者是一个内部 类型的对象,该运算符函数必须作为一个友元函数来实现。
- (7) 当需要重载运算符具有可交换性时,选择重载为友元函数。
2.2随堂笔记
complex c1(2,1);
complex c2(5);
c2 += c1;
操作符作用在左边的操作数上。
class的成员函数带一个隐藏参数,this指针,不能再代码中体现,this指针参数位置不一定是第一个,取决于编译器。
为了满足 c3 += c2 += c1;重载 += 时必须有返回值;
3.参数传递与返回值
const member functions 常量成员函数
对于class里面的函数,分为会改变class内数据内容的和不会改变class内数据内容。不会改变class内数据内容的函数,马上加上const。
参数传递 pass by value vs. pass by reference(to const)
传reference底层就是传指针,速度快,形式好看,如果不想被改变,则加上const。
返回值传递,pass by value vs. pass by reference(to const)
对于函数体内创建的local object,不能return by reference,因为他的生命周期在函数返回后就终止了。此时若用reference返回,则会返回坏的内容。
friend(友元函数)
友元函数可以直接访问class的private数据。
相同class的各个objects互为friends(友元)。
4.写一个class应该注意的地方
- 1、数据一定放在private里面。
- 2、参数尽量以reference来传递,看情况加上const。
- 3、返回值尽量也以reference来传递,可以的情况下。
- 4、class内的各种函数,需要加const的一定要加上const。
- 5、构造函数的初始化数据使用initialization list。