1、一般来说,类的私有成员只能在类的内部访问,类外的函数是不能访问它们的。 但是,可以将一个函数定义为类的友元函数,这时该函数就可以访问该类的私有成员了。
- 友元之普通函数(非成员函数,自由函数)
- 友元之成员函数,,用到类的前向声明---->声明的是一个不完全的类。
- 友元之类。friend Line 或friend class Line
- 友元关系是单向的关系。不能传递,是不可继承的。
- 只有两个有强相关的时候,才使用,一般不使用。
2、运算符重载
总原则、目的:为了使对用户自定义数据类型的数据的操作与内置的数据类型的数据的操作形式一致。--->例子:复数。
内置类型 (8 种):
>int /char / short / long /float /double /bool /
> C++ 定义内置类本身具有复制语义 。值语义(复制语义),不能进行(禁止)复制的类,对象语义
- 不能重载的运算符有: (带点的不能重载)
- 成员访问符 .
- 成员指针访问运算符 .*
- 域运算符 ::
- 长度运算符 sizeof
- 条件运算符号 ?:
重载规则
- 不能对内置类型的运算符进行重载
- 重载操作符必须具有一个自定义类类型或者是枚举类型的操作数 int operator+(int, int);//不能重载
- 优先级和结合性是固定的,操作符的优先级、结合性或操作数个数不能改变 X == Y + Z;
4. 如果一旦重载了逻辑运算符,则它不再具备短路求值特性 ,不建议重载逻辑运算符。重载操作符并不保证操作数的求值顺序 && || 。
5、对于绝大多数可重载操作符来说,两种重载形式都是允许的。但对下标运算符[] 、赋值运算符=、函数调用运算符()、指针运算符->,只能使用成员函数形式。
运算符重载的方式分为下面3种:
1. 采用普通函数(自由函数)的重载形式 --> + - * /
2. 采用友元函数的重载形式 ---> + - * /
3. 采用成员函数的重载形式
---> 复合运算符 += ++ -- 复制运算符 = operator new/delete
函数调用运算符 () --->函数对象
[ ] --->下标访问运算符
右值,在操作符的右边,同理,左值。
- += -= 建议使用成员函数重载。 -->对于复合赋值运算符。自增,自减运算符。(对于重载来说,前置++,更容易实现,可以返回& ,complex & operator++( )。对于后置++ 不能返回引用了,写成,Complex operator ++(int) //int不代表一个参数,只是作为一个标志,与前置++做区别) 所以,能够使用前置的时候,就不要使用后置。(因为前置++ 的效率高于后置++)特别是在迭代器的时候。
- 对于+ - * 、可以使用自由函数或friend重载。
- 对于绝大多数可重载操作符来说,两种重载形式都是允许的。但对下标运算符[] 、赋值运算符=、函数调用运算符()、指针运算符->,只能使用成员函数形式。
- 函数调用运算符()同样只能重载为成员函数形式,返回类型 operator()(arg1,arg2,……) 参数个数可以有多个,没有限制。 这里的对象是一个函数对象,它是重载了函数调用运算符的类创建的对象。
- 下标运算符[ ] 返回类型& operator[ ](参数类型); 下标运算符的重载函数只能有一个参数,不过该参数并没有类型限制,任何类型都可以,如果类中未重载下标运算符,编译器将会给出下标运算符的缺省定义。
3、运算符重载--->续
- 输入>> 输出 <<的重载
-
- 输入输出流运算符第一个参数要求是流类型(ostream , istream) ,而如果作为成员函数存在,则它的第一个参数是this 指针,故不能以成员函数重载,建议以自由函数,且为友元函数。
- 流对象是不能进行复制的。故只能返回引用。不能返回一个类型。
- 注意参数类型时i,不修改的才能加const ,要修改的不能加const 。
- 指针运算符 -> 和 * 的重载
-
- 在智能指针时,还会讲解,有应用。
- 类型转换
-
- 转换场合有:
-
- 赋值转换 表达式中的转换 显式转换
- 函数调用,
- 传递参数时的转换
- 转换方向有:
-
- 由定义类向其他类型的转换
- 由其他类型向定义类的转换
- 由其他类型(如int、double)等向自定义类的转换是由构造函数来实现的
- 类型转换函数
-
- 在类中定义类型转换函数的形式一般为:
- operator 目标类型名();
- 有以下几个使用要点:
- 转换函数必须是成员函数,不能是友元形式。
- 转换函数不能指定返回类型,但在函数体内必须用return语句以传值方式返回一个目标类型的变量。
- 转换函数不能有参数
- 尽量不要多使用,违反直觉思维的。
- 类作用域
-
- 在类中定义的成员变量和成员函数的作用域是整个类
- (1)全局作用域 在函数和其他类定义的外部定义的类称为全局类
- (2)类作用域(类中类、嵌套类) 一个类可以定义在另一类的定义中,这是所谓嵌套类,---> 嵌套类可以用来封装。
- PIMPL设计模式 ---->了解一下。
-
- 实现信息隐藏
- 能够保证接口的稳定性,内部的实现做任何变化都不会影响使用该头文件的程序,可以最小的变化做到平滑升级库的目的。
- 减小编译的依赖。
- 块作用域 类的定义在代码块中,这是所谓局部类,该类完全被块包含,其作用域仅仅限于定义所在块,不能在块外使用类名声明该类的对象
4、写时复制 (Copy -on -Write)
----> 编程界“懒惰行为”——拖延战术的产物
GNU-GCC 5.X.X ---> String类也有应用,SSO (短字符串优先)short string optimazation
GNU-GCC 4.X.X ---->COW ( 写时复制 )
表达复制语义时,才是写时复制。
比如:fork( )中就有体现。
- 我们把引用计数放在堆上面,采用将引用计数放在字符串的‘\0’的后一位。
- 引用计数加减一,用函数封装,在复制构造函数时需要先浅拷贝,在加一。
- 赋值运算符,如果不是自赋值,先左操作数,引用计数减一,判断是否为0,若是,在delete ,浅拷贝,在右操作数计数加一。在返回。
- 用 [ ]重载后,用来修改,如果引用大于1,就原来引用减去1,然后开空间,在去复制,,在浅拷贝(修改指向),在初始化新的引用计数。