zoukankan      html  css  js  c++  java
  • [GeekBand] C++学习笔记(1)——以复数类为例

    本篇笔记以复数类(不含指针的类)为例进行面向对象的学习

    =========================================================

    复数类的声明:

     1
    				class complex
    

     2
    				{
    

     3
    				public:
    

     4   complex (double r = 0, double i = 0): re (r), im (i) { }
    

     5   complex& operator += (const complex&);
    

     6   complex& operator -= (const complex&);
    

     7   complex& operator *= (const complex&);
    

     8   complex& operator /= (const complex&);
    

     9
    				double real () const { return re; }
    

    10
    				double imag () const { return im; }
    

    11
    				private:
    

    12
    				double re, im;
    

    13
    			

    14   friend complex& __doapl (complex *, const complex&);
    

    15   friend complex& __doami (complex *, const complex&);
    

    16   friend complex& __doaml (complex *, const complex&);
    

    17 };
    

    =========================================================

     一、构造函数

      1)C++列表初始化

        complex(double r = 0,double i = 0)

        :re(r),im(i)

        {}

    • 一般情况下,构造函数写在public区域.  一个例外情况是Singleton设计模式.
    • 构造函数没有返回值.
    • 只有构造函数可以使用 : () 构成的列表初始化语法. 
    • C++的初始化工作可以分为创建阶段和操作阶段.创建阶段是在列表初始化结束之后就结束了;在大括号中的所有操作都是对象已经被创建之后再去进行的操作.理论上讲,可以栽{}使用assign操作代替列表初始化.然而,这样做相当于是放弃了创建阶段的操作.此外,如果对象中含有const变量,是不能在创建阶段结束后再去修改的,这种情况下必须使用列表初始化

      2) 构造函数的调用方法

        构造函数不显式调用,有以下三种方法。

    • complex C1(2,3);
    • complex C2;//在构造函数定义中,实部和虚部均有默认值0;
    • complex* C3 = new complex(4);//这里并不是进行了一次显式调用,而是声明了一个匿名对象;声明匿名对象的方式就是采用之前的两种方法,区别在于其不存在变量名。如果不是通过new创建的堆变量,栈中的匿名对象的生命周期仅为当前行。 

      3) 构造函数的重载

        在C语言中不允许出现同名函数。但是在C++中,允许函数同名,但必须保证根据参数的不同可以找到唯一的一个函数实体与之对应,不能产生二义性。与大部分成员函数一样,构造函数可以被重载。

        * 在编译时,对于重载函数,编译器会给予其一个real name,例如complex::complex@doubledouble,其中包含的参数的类型信息。

         也就是说,重载后的函数实际上名字是不同的。

        * 当构造函数具有默认值是,应特别注意是否会产生二义性。

      4) 默认构造函数

        当没有定义构造函数时,C++会提供一个默认构造函数。默认构造函数不接受任何参数,不进行任何操作。

        只要用户定义了一个构造函数,不论其参数为何,C++将不再提供默认的构造函数。

      5) 构造函数在private区时的情况(Singleton设计模式)

          

     1
    				class A{
    

     2
    				public:
    

     3
    				static A& getinstance();
    

     4
    				    setup() {...};
    

     5
    				private:
    

     6
    				    A();
    

     7     A(const A & rhs);
    

     8
    				    ...
    

     9
    				};
    

    10
    			

    11 A& A::getinstance()
    

    12
    				{
    

    13
    				static A a;//静态变量
    			

    14
    				return a;    
    

    15 }
    

      调用方法:

        A::getinstance().setup();

        //这种设计模式下,永远只有一个实例。

       

    二、const 关键字

      double real () const { return re; }

      在参数表后的const,表示当前函数不会改变对象内部成员变量的值。  

      complex& operator += (const complex&);

      在参数前的const,表示当前参数对于成员函数是一个常量变量,且不会被改变。

      * 返回值也可以被指定为const类型。

        

      不加const会产生的后果:

      e.g.

        const complex C1(2,1);

        cout<<C1.real();

        如果在real函数定义时没有加上const关键字,C1.real()就不能执行,因为只看这一接口无法保证C1的数据成员不会被real函数修改。

       

    三、 传值

      三种传值方案:值传递、引用传递、const引用传递

      1)参数传递

    • 尽量不使用值传递的传递方案,因为这样复制的效率太低了。引用是C++中提供的用来代替指针的一种方案。相比于指针,其好处是使用更简单,用户可以像值传递时一样使用它,而不用考虑其背后的实现方法;而使用指针时,用户必须知道这是一个指针,又涉及了&操作和*操作,不易于使用并且很容易误操作(指针使用不当产生的内存泄漏、溢出等问题)。
    • const 引用传递的目的在上一小节中已经解释过,就是为了解决常量对象的调用问题。
    • 在传参时,建议不论什么参数都采用引用传递的方式。

      2)返回值传递

    • 仍然推荐采用引用传递,但不再是不论什么情况都采用引用传递。
    • 采用引用传递的另一个好处是返回值可以作为左值继续进行操作。如a+b+c。
    • 不能返回引用的情况:
    1.  
      1. 变量为栈空间内的局部变量。
      2. 要返回的对象为一const对象。这种情况下,引用可能会带来对象内容的更改,所以只能采用值传递或const引用传递

      Appendix——关于引用:

      1)单纯引用的语法

        &ref = obj;

        ref成为obj的别名。作为参数传递时,就可以按照这种方式理解。(实际上并不是这样的操作,传参相当于构造,是在第一阶段完成的,而赋值是在第二阶段。不过如第一小节所述效果是一样的)

      2)作为返回值时。

        e.g. int & getnum(&i) {return i;}

        若使用int a=getnum(i):则相当于先调用了getnum(i),其返回就是i的实体;然后再用i进行了复制构造操作。也就是说,a和i是两个变量。

        若使用int&a = getnum(i):则相当于a也是i的别名。然而这要求用户知道返回方式为引用传递,因为引用并不能指向一个值。

        若getnum(i) = 1;则是对i进行了赋值操作。getnum(i)就是i的一个别名。(相当于是一个匿名变量。)

        事实上,返回的就是对象i;至于是引用传递还是值传递,则是接收方式的不同。

      注:引用不能无初始化存在,也不能被修改,可以视为常指针。

       

    四、封装的后门——友元

      如果一个函数是类的友元函数,则该函数可以直接使用类的private成员,就像是类的成员函数一样。

      友元的两个存在方式:

    1. friend关键字在类的声明中声明。
    2. 同一个class的各个object互为友元。

        这就是为什么在运算符重载时,可以直接使用C2.real变量。也就是说,类的成员函数中的参数如果是同类对象,可以直接使用它的private对象。

       

    五、特殊的函数重载——运算符重载

      运算符就可以视作一个函数。

      1) 作为成员函数重载(一般为单目)

      以+=运算符为例:

          

     1 inline complex&
    

     2 __doapl (complex* ths, const complex& r)
    

     3 {
    

     4   ths->re += r.re;
    

     5   ths->im += r.im;
    

     6   return *ths;
    

     7 } 
    

     8 inline complex&
    

     9 complex::operator += (const complex& r)
    

    10 {
    

    11   return __doapl (this, r);
    

    12 }
    

    • 编译器如何看待操作数?

        C2+=C1;《=》C2.operator +=(this , const complex &r);//其中r为C1的别名;

        * 任何的成员函数都有隐藏的this指针,这个指针只在C++的后台中才会体现,我们使用C++时不可见。

    • 返回引用

        考虑到存在连续操作的可能,应该为原类型的引用。

    • 运算符的结合方式:

        在连续操作时,涉及到运算符的结合方式。

        左结合,如:<<,[],+,-等;a+b+c <=> (a+b)+c

        右结合,如:=,+=,-+等;a=b=c <=> a=(b=c)

       

      2)作为非成员函数:

         以+为例的双目函数:

    1
    				inline complex
    

    2
    				operator + (double x, const complex& y)
    

    3
    				{
    

    4
    				return complex (x + real (y), imag (y));//返回一个匿名临时变量
    

    5 }
    

        * 返回值一般采取值传递

        e.g.

          对于a=b+c,若b+c采取了引用传递,局部变量将会消亡。

         

        单目非成员函数(操作数仅为后值),如取负。

    1
    				inline complex
    

    2
    				operator - (const complex& x, double y)
    

    3
    				{
    

    4
    				return complex (real (x) - y, imag (x));
    

    5 }
    

        注:如果接口合适,非成员函数的操作符重载并不需要在类的声明中体现出来。

         

        *输入输出流相关函数的重载:(由于第一操作数为ostream对象,因此并不能作为成员函数。)

          

    ostream & operator << (ostream& os, const complex &x)
    

    {
    

    
    					return os<<'('<<real(x)<<','<<imag(x)<<')';        
    

    }
    			

      

  • 相关阅读:
    [LeetCode] 286. Walls and Gates 墙和门
    [LeetCode] Sparse Matrix Multiplication 稀疏矩阵相乘
    [LeetCode] 314. Binary Tree Vertical Order Traversal 二叉树的竖直遍历
    [LeetCode] 337. House Robber III 打家劫舍之三
    [LeetCode] Palindrome Pairs 回文对
    [LeetCode] 302. Smallest Rectangle Enclosing Black Pixels 包含黑像素的最小矩阵
    Nginx安装及配置详解
    Spring Boot项目属性配置
    maven仓库阿里云镜像配置
    maven仓库阿里云镜像配置
  • 原文地址:https://www.cnblogs.com/shawnChi/p/5685933.html
Copyright © 2011-2022 走看看