C++程序设计特别版学习笔记(二)

                                前言

         本序列笔记是来自阅读裘宗燕翻译的C++程序设计特别版的总结。

  运算符号重载

不能重载的运算符
  ::(作用域解析)
  。(成员选择)
  。* (通过成员的指针做成员选择)
一元运算、二元运算
  [1]aa@bb
  可以解析
 Operator@ (aa,bb)            ——>非成员函数
  aa.Operator@(bb)            ——>成员函数
 
 [2]@aa
 operator@(aa)
 [3] 运算符的预定义
   operator = ,operator[] ,operator(),operator-> 只能作为类的非静态成员函数,这样才能保证第一个运算对象一定是一个左值
 
 [4]x@y 解析
n             查找对象x的成员函数
n             围绕x@y的环境中查找
n             在X,Y的空间中查找
n             注意歧义
 
混合模式、隐式转换
n                构造函数、转换
      Ex:
         class complex
          {
            //…
            complex( double);
            //…
          }
      这样就存在一个从内部类型 double complex 的转换。
      在表达式中,通过隐式、显式转换的都是临时对象,将在最早可能的时候被销毁。
      任何用户定义的类型转换,都不会用到 -> (.) 左边。即使是在隐含的情况下也是如此。
       通过构造函数的机制,可以实现从一个类型 到另一个类型(该类型拥有另一个类型的成员对象)的转换。
n                初始化
      一般是通过 复制构造函数,可能需要类型转换。
n                显式构造函数,定义单个参数的构造函数,就存在一个该参数类型到该类的隐式转换,如果要防止这样的事件发生 可以在该构造函数声明explicit
转换运算符号
 通过构造函数去刻画类型转换确实很方便,但是也意味着存在一些并不是我们希望的情况。构造函数不能刻画
[1] 从用户定义类型到内部类型的转换,因为内部类型不是类。
[2] 从新类型 到某个已有的类型的转换(而不是修改那个已有的类型)。
  
   通过以下的技术,就可以实现 从 X 类型 到 T 类型的转换。
         operator X::T() 是成员函数
 通过这样,就任何需要转换的情况下从X 到T的转换。
[3] 一个类 的类型转换 可以通过 用户定义转换(构造函数机制),或者用户定义转换运算符,但是不能同时存在这两种情况,因为这样会存在歧义。
 
友元
       一个常规的成员函数声明描述了三件在逻辑上相互不同的事情:
[1] 访问类声明的私有部分。
[2] 位于类的作用域中。
[3] 需要一个对象去激活(有this 指针)
声明为 static 成员函数具有[1]、[2]的功能,声明为友元具有[1]的功能。
 
   友元注意事项:
[1] 友元可以在类的任何部分声明。
[2] friend 类只应该用于那些密切相关的概念,常常会做出这样的选择,是将一个类声明一个类的成员(嵌套类),或是友元。
 
   友元的寻找
[1]像成员的声明一样,一个友元声明不会给外围作用域引进一个名字。
   Ex:
      class Matrix{
      friend class Xfrom;
      friend Matrix invert(const Matrix&);
      //…
      };
      Xfrom x;              // 作用域内无Xfrom
      (Matrix *p)(const Matrix&) = &invert() // 错误,作用域内无invert ()
 
     对于大型程序和大的类,一个类不能“默不做声地”给它的外围作用域加入一些名字。
[2] 一个友元类 或者是外围作用域里做先行声明,或者是将它作为友元的那个类的直接外围的非类作用域里定义的。
Ex:
Class X{/*……*/}; // 是Y 的友元
Namespace N
{
   Class Y{
      Friend class X;
      Friend class Z;
      Friend class AE;
      };
   Class Z{
     //……
      };           // 是Y 的友元
}
Class AE{/*……*/}; // 不是Y 的友元
 
 
    友元函数
[1] 一个友元函数可以像友元类那样的声明。此外还可以通过它的参数找到它,即使它并没有在外围最近的作用域里声名。
[2] 一个友元函数或者需要在某个外围作用域里显式声明,或者是以它的类或者该类的派生类作为一个参数,通过参数去寻找它,否则就无法利用这个友元了。
     Ex
     // 假定作用域里 无f ()和h (。。。)
     Class X{
            Friend void f();           // 无用的
            Friend void h(const X&); // 可以通过参数找到,通过参数解析(调用函数的机制)[ 没有通过函数名称解析]
              }
        Void g(const X& x)
        {
            f(); //There is no f();
            h(x); //Ok
        }
 
成员友元
  Ex:
     Class X{
     //…
       X(int);
       Int m1();
       Int m2()const;
       Friend int f1(X&);
       Friend int f2(const X&);
       Friend int f3(X);
       };
 
        Void g()
       {
          99.m1();      //Eorro
          f1(99);        //Eorro
          f2(99);        //ok
          f3(99);        //ok
       }
 
[1] 成员函数只能通过有关类的对象去调用,用户定义的转换不会用到 ->(.)的左边
[2] 隐式转换不会用到非 const 引用的初始化,但是可以用到 const 引用的初始化。
[3] 如果希望某个运算的所有运算对象都能允许隐式转换,实现它的函数就应该作为
   非成员函数,取const X& 参数,非引用参数。
基本运算符
       一般说来,对于类型 X,复制构造函数 X (const X& 负责完成从同类型X的一个对象出发的初始化。
       强调赋值和初始化是不同的是决不过分的。
复制构造函数利用在 参数传递、值返回、异常的原理
[1] 参数传递,给形式参数初始化
[2] 异常也一样
[3] 值返回,是给一个临时对象初始化
下标
 函数 operator[]可以用于为类的对象定义下标运算的意义。 operator[] 的第二个参数([下标])可以具有任何类型。
函数调用
   函数调用,即记法形式 expression(expression-list),可以解释为一种二元运算,其中 expression 是左值对象,expression-list 是右运算对象。这一运算符()可以通过重载方式。
   形式 operator ()(arg - list
   函数对象
   Class Add{
     complex val;
Public:
 Add (complex c);
 Add (double r,double i);
 Void operator()(complex& c)const;
};
间接
 间接运算符—> 可以被定义为一个一元的后缀运算。
  Ex:
   Class Ptr{
   //……
   X* operator ->();
   };
 
 Void f(Ptr p)
 {
      p->m = 7;          //(p.operator->())->m =7
      X* q1 = p->;    //error
      X* q2 = p.operator ->();//ok
 }
增量(++) 和减量(――)
   Ex:
   Class Ptr_to_T{
T* p;
T* array;
int size;
public:
Ptr_to_T& operator ++(); // 前缀
Ptr_to_T operator ++(int ); // 后缀,是个左值
Ptr_to_T& operator ―― ();
Ptr_to_T operator ――(int);
T& operator* ();
};
派生类
继承(派生)
 是对类功能的一种扩展行为。
 [1] 派生类可以使用基类的公用部分的和保护部分,但不能使用私有部分。
 [2] 派生类 对象 给基类对象赋值存在对象切片问题。
 [3] 一般说来,最清晰的设计是派生类 只使用它的基类的公用成员。
 
构造函数、析构函数
  有些派生类需要构造函数,如果某个基类中有构造函数,那么就必须调用这些构造函数中的某一个。默认构造函数可以被隐含调用,但是如果该基类的所有构造函数都有参数,那么就必须显式调用。
 
 复制
 类对象的复制由复制构造函数 和赋值操作定义的。
 
 复制构造函数、赋值操作绝对不是继承的。
 
对于给定的一个类型为Base* 指针的指向
  [1] 保证被指的只能是一个惟一类型的对象(没有派生类吗?)
[2] 在基类里安排一个类型域,供函数检查。在检查后进行类型转换
[3] 使用 dynamic_cast
[4] 使用虚函数
Ex:
Class Employee
{
//…
enum Empl_type{M,E};
Empl_type type;
//…
};
类型域很容易出错误 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值