zoukankan      html  css  js  c++  java
  • 侯捷《C++面向对象编程》笔记第(一)周——超经典

    1.推荐书籍

    《EFFECTIVE C++改善55个方法》

    《STL源码剖析》

    《THE C++ STANDARN LIBRARY 》

    《C++ Prinmer》

    2.头文件与类声明

    (1)

    类根据里面是否包含指针,分为object based(面向单一class设计)   and  object oriented(面对多重class的设计,classes和classes之间的关系);本课程将分别就两种类型举例,分别写一个complex类(复数),和string类(字符串)。

    (2)头文件防卫式声明

    防止再次调用头文件的时候,程序重复访问头文件主体

    #ifndef __COMPLEX__//第一次没有定义该类型,则定义并访问主体,如果第二次访问,则不需要再访问,可用于所有头文件文件
    #define __COMPLEX__
    
    class complex
    {
        ......
    }
    
    #endif

    (3)头文件布局

    a. class template(模板)简介(开眼界了)

    如果你的类里面的变量类型暂时还没确定,或者说你想随机应变,一会儿是int,下一个又可能是double 等等,可以用模板来泛化定义,使用的时候再定义

    template <typename> T
    
    class comples
    
    public:
    
    {
        complex (T r=0, T i=0):re(r), im(i){}
    }
    
    private:{ T re, im;}
    
    //main.cpp
    complex<double > c1(2.2,4.3);
    complex<int > c2(1, 3);

    3.构造函数

    (1)inline(内建)函数

    定义:在class本体内定义的函数称为inline function(但最后是否真的是内联函数,由编译器决定,我们未知,一般太复杂的函数不会被认定为Inline)

    (2)访问级别

    private:数据或函数部分,不想让外界看到的数据,封装起来;

    public:让外界看到。两者可以在一个类body内多次使用

    protected:~

    (3)构造函数

    a.一旦定义,自动启动;

    b.构造函数(其他函数也可以)可以有默认值,如果创建的时候未定义,则用default argument;

    c.构造函数不需要返回类型;

    d.不带指针的类,多半不需要写析构函数;

    e.构造函数(其他函数也可以)常常用overloading(重载),用于多种初值设定想法;

    class complex
    {
    public:
        complex(double r=0, double i=0):re(r), im(i) {}//构造函数独有的初始化写法,更大气,等同于在{}里将传值一一赋给参数。这种赋值效率更高
    /*
        complex (double r=0, double i=0)
        {re=r; im=i;}
    */
    //构造函数常常被重载,虽然函数名在编码者看来看似相同,但在编译器看来是不同的
    //如果第一个构造函数已经有默认值,下面这个重载的构造函数是不允许的,因为在新建一个不指定值的类时,编译器会不清楚应该用第一个还是下面这个,因为两个都可以
        //complex (): re(0), im(0) {}
        complex & operator +={const complex &};
        double real() const { return re;}
        double imag() const { return im;}
    
    private:
        double re, im;
        friend complex& _doap1 (complex *, const complex &);
    };
    
    //main.cpp
    
    {
        //新建类的几种类型
        complex c1(2,1);
        complex c2;
        complex *p = new complex(4);
    
    }

    4.参数传递与返回值

    (1)ctors 放在private区

    存在这种写法,叫Singleton

    (2) 常量成员函数

    a. 重要!!!在函数后,对于不会改变数据值的函数要加const。如果不加,遇到以下情况,编译器会报错!

    class complex
    {
    public:
        complex(double r=0, double i=0):re(r), im(i) {}
        double real() const { return re;}//函数不会改变数据大小,const位置在函数定义后,在大括号前面
        double imag() const { return im;}//同上
    
    private:
        double re, im;
    };
     //main.cpp
    
    {
        complex c1(2,1);
        cout<<c1.real();
        cout<<c1.imag();
        //使用者如上使用没有问题,但是下列使用则会报错
        
        const complex c1(2,1);
        cout<<c1.real();//error!因为使用者想定义一个常量,也就是值不能被改变,但是该句调用时,
    //如果原类函数没有加const,那么就说明它可能会改变数据的值,但是我前面又定义了常量,不能改变数据值,
    //所以编译器就会报错。解决办法就是在不改变值的函数定义时都加上 const。
        cout<<c1.imag();    
    }
    
    

    (3)参数传递 :pass by value vs. pass by reference (to const)

    a. 参数传递时,尽量传引用。引用在底部就是一个指针,所以传值很快。良好习惯:所有传值都尽量用引用。

    (32位编译器:指针4个字节,字符1个字节,整型4个字节,double 8个字节,float 4个字节)

    b.传参数引用的同时,不想自身的值被改变时,加const

    class complex
    {
    public:
       //...
        complex& operator += (const complex&);//这里的参数传值选择的引用,并且由于不想参数在此函数中
    //被改变,所以选择加const.
       
    private:
        
    };
    //main.cpp
    
    ostream&
    operator <<(ostream& os, const complex& x)//同理,注意第一个参数未加const,原因后续道来
    {
        return os<<'('<<real(x)<<','<<imag(x)<<')';
    }

    c.return by value vs. return by reference 返回值也尽量传引用

    complex& operator += (const complex&); //return by reference

    double real() const{ return re; } //return by value

    double imag() const{ return im; } // return by value

    d.传引用也只是尽量,不一定所有场合都适用,后续会讲。

    e.友元。对于其他外界函数,不能调用private成员,但是对于友元函数,就允许用private里的数据

    private:
        double re, im;
        friend complex& _doap1 (complex *, const complex &);//友元函数
    };
    
    inline complex&
    __doap1 (complex * ths, const complex& r)
    {
        ths->re += r.re;
        ths->im +=r.im;
        return *ths;
    }

    f. 相同class的各个objects 互为friends(意思就是类本体里面的函数可以调用private成员,而这样可行的原因就是前面所叙述的那样)

    class complex
    {
    public:
       //...
        int func(const complex& param)
        {  return param.re + param.im; }//该成员可以调用私有变量,是因为其默认为友元函数
    private:
        double re,im;
    };
    

    g. 总结:

    数据要放在private里;

    参数尽量以reference传;

    是否加const; 返回值也尽量以reference传(允许的情况下);

    类的本体里的函数应该加const的要记得加; 

    构造函数的初始化的专业写法。

    h.补充:class body 外的各种定义:什么情况下可以pass by reference 什么情况下可以 return by reference

    不能pass by reference 的情况:函数内部会将变量改变,则不加const;函数不改变变量的时候,则加const;

    complex & ___doap1(complex* ths, const complex& r)//第一个变量在函数内部会被改变,所以不加const, 第二个不会被改变,所以加const.
    {
        ths->re +=r.re;
        ths->im +=r.im;
        return *ths;
    
    }

    不能return by reference的情况:返回值是在函数内部定义的一个变量则不能,因为该变量会随着函数结束而消失。若返回值是已经存在的,则可以返回引用。

    complex& ___doap1(complex* ths, const complex& r)//文件名的 & 表明返回的是引用
    {
        ths->re +=r.re;
        ths->im +=r.im;
        return *ths;//因为ths是外部已经存在的变量,所以可以返回引用
        //下列情况则不能传引用
        /*
        complex mm;
        mm = ths->re + r.re;
        return mm;//mm会随着函数结束而消失,所以不能传引用
        */
    
    }

    5.操作符重载与临时对象

    (1)操作符重载-1,成员函数

    a. 比如重载 += 操作符,首先我们要知道所有的成员函数一定带有一个隐藏的参数this, 即指向它本身的一个指针,这个指针在函数定义的时候不能出现,但在函数内部可以直接使用,指向的就是调用该函数的那个变量。如 c2 += c1; 则操作符重载中this 指向c2。

    inline complex&
    complex::operator += (const complex& r)//重载
    {
        return __doap1 (this, r);//this 默认指向使用的该符号的变量
    }
    
    complex & __doap1(complex* ths, const complex& r)//这个函数也可以直接在上面函数定义,但为防止以后还会继续用,则拎出来单独定义。
    {
        ths->re +=r.re;
        ths->im +=r.im;
        return *ths;
    }//所有左右形式的符号,都可以这样定义
    //main.cpp
    complex c1(2,1);
    complex c2(2,3);
    c2 += c1;//+=操作符需要重载

    b. 传递者无需知道接受者是以reference形式接受

    complex & __doap1(complex* ths, const complex& r)//开头的& 是接受端
    {
        ths->re +=r.re;
        ths->im +=r.im;
        return *ths;//可以看到返回的是指针指向的值,但是接收端是返回的引用,但是我们可以不必在意,这样是可行的
    }

    c. 对于c3+=c2+=c1的考量,函数返回不能设为void

    complex& __doap1(complex* ths, const complex& r)//开始的complex不能为void,如果只是c1+=c2,这样的操作是可以的,c2的值得到了改变,但是如果c1+=c2+=c3这样的操作就会出错,因为c3和c2加完后没有返回值,无法继续和c1相加。
    {
        ths->re +=r.re;
        ths->im +=r.im;
        return *ths;
    }

    e class body之外的各种定义,成员函数(函数前面带类的名称 complex::),全域函数(函数名前面没有类名)

    (2)操作符重载-2,非成员函数

    a. 为了应对使用者的几种可能做法,对应开发三个函数;

    b.再次提醒:以下这些重载函数绝不能return by reference,因为传递的是函数内新建的临时对象;

    c.特殊语法(临时对象): typename(  c  );内部也可以加减,相当于定义了一个新的该类型变量并赋值。它的生命到下一行就会结束,并且没有名称。

    inline complex
    operator + (const complex& x, const complex& y)//重载复数加复数
    {
        return complex (real(x)+real(y),
                        imag(x)+ imag(y));//临时对象,生命下一行就会结束,但是因为已经将值return,所以可行。
    }
    
    inline complex
    operator + (const complex& x, double y)//重载复数加浮点数
    {
        return complex (real (x) + y, imag (x));//新建临时对象
    }
    
    inline complex
    operator + (double x, const complex& y)//重载浮点数加复数
    {
        return complex (x+ real (y), imag (y) );//新建临时对象
    }
    
    //main.cpp
    {
        complex c1(2,1);
        complex c2;
    
        c2 = c1+c2;//复数加复数
        c2=c1+5;//复数加浮点数
        c2=7+c1;//浮点数加复数
        //......
    
        //临时对象举例
        complex();//新建一个值为default value的临时对象,下一行就会消失;
        complex(2,1);//新建一个实部为2,虚部为1的临时对象。
    }

    e. 操作符重载 正号、符号;编译器看参数个数来判断是正号还是加号

    f. " << "输出符号的重载,只能作为全局函数来定义"<<"的类型是"ostream"

    # include <iostream.h>//该操作符重载必须是全局函数
    ostream&//返回不能用void,因为有可能连续调用<<,如式3
    operator << (ostream& os, const complex& x)//可以看到第一个参数不加const,因为每传一个值,const的状态就会被改变,所以它不是常量
    {
        return os<<'('<<real (x) <<','<<imag(x) <<')';
    }
    //main.cpp
    {
        complex c1(2,1);//1
        cout<<conj(c1);//2
        cout<<c1<<conj(c1);//3
    }

    第一周完!!!

    Higher you climb, more view you will see.
  • 相关阅读:
    2013暑假集训B组训练赛第二场
    2013暑假集训B组训练赛第二场
    2013暑假集训B组训练赛第二场
    2013暑假集训B组训练赛第二场
    SPOJ ANARC05H 计数DP
    HDU 2586 LCA-Tarjan
    POJ 1330 LCA最近公共祖先 离线tarjan算法
    Codeforces 176B 经典DP
    UVA 10564 计数DP
    HDU 4901 多校4 经典计数DP
  • 原文地址:https://www.cnblogs.com/yyfighting/p/12500647.html
Copyright © 2011-2022 走看看