• c++0.1-----基于对象知识大综合(非指针篇)


      本文包含知识点有:预编译,访问权限,常成员函数,内联函数,构造函数,运算符重载函数,友元。

    以代码为示范:

      文件名:ccompex.h  

      文件内容:定义一个简单的复数类。

     1 #ifndef __CCOMPLEX__
     2 #define __CCOMPLEX__
     3 #include <iostream>
     4 #include <ostream>
     5 using namespace std;
     6 class complex{
     7 friend complex& __doapl(complex& com1,const complex& com2);
     8 public:
     9     inline const double& real() const {return re;}
    10     inline const double& image() const {return im;}
    11     inline complex(double r=0.0,double i=0.0):re(r),im(i){}
    12 private:
    13     double re;
    14     double im;
    15 };
    16 
    17 inline complex & __doapl(complex &com1,const complex& com2)
    18 {
    19     com1.re+=com2.re;
    20     com1.im+=com2.im;
    21     return com1;
    22 }
    23 
    24 inline complex & operator+=(complex &com1,const complex& com2)
    25 {
    26     return __doapl(com1,com2);
    27 }
    28 
    29 inline complex operator+(const complex& com1,const complex& com2)
    30 {
    31     return complex(com1.real()+com2.real(),com1.image()+com2.image());
    32 }
    33 inline complex operator+(const complex& com1,const double& dou)
    34 {
    35     return complex(dou+com1.real(),com1.image());
    36 }
    37 inline complex operator+(const double& dou,const complex& com1)
    38 {
    39     return complex(dou+com1.real(),com1.image());
    40 }
    41 
    42  ostream& operator<<(ostream &os,const complex& x)
    43 {
    44     os<<'('<<x.real()<<','<<x.image()<<')'<<endl;
    45 }
    46 
    47 #endif // __CCOMPLEX__

     

     

    这47行代码包含了几个c++精髓,下面让我娓娓道来~~~

    一.预编译:#ifndef---#define---#endif 与#include

    #ifndef---#define---#endif 代码在第1,2,47行,功能是避免头文件重复调用,在编译的时候合理将代码替换过来。

    头铁不信系列:去掉#ifndef---#define---#endif 后,再多次在主文件包含这个头文件,进行编译,编译器给出以下错误信息:

    error: redefinition of 'class complex'

    错误解析:以上错误信息的意思是说对complex类重定义了。也就是说包含多次该文件,若没有预编译的功能,错误就会出现。

     

    #include代码在第3,4行,表示引入一些头文件。

    使用方法:系统头文件用<>包住,同时,不需要加.h后缀哦。例如:#include<iostream>

            自定义头文件用" "包住。例如:#include "ccomplex.h"。

     

    二.访问权限:

     数据成员尽量都设置为私有变量(private),这样它就不会被外界访问。

    成员函数方面,若是提供给用户访问的接口,则必须设置为public。若只是供这些内部调用,则设置为private比较好。

     

    三.常成员函数:

    常成员函数代码在第9,10行,其形式是在函数体大括号前面加上一个const限定符。其功能为强制命令该成员函数real()和image()不能够改变数据成员的值。若是头铁不信,只能bug伺候。

    例如:我在const函数里修改数据成员,如下:

    inline const double& real() const { re=1; return re;}

    编译器提示错误信息:

    error: assignment of member 'complex::re' in read-only object

    意思是:给complex类型的只读对象的数据成员re赋值了,产生了错误。

    由此可见,系统将const限定的成员变量限定成为只读的了,不能进行修改。那么这也就产生了一个问题,返回值得形式该怎么确定。

    如果我返回的double的引用会怎样呢?例如:

    inline double& real() const {return re;}

    好吧,依然会报错,看看编译器暗示了什么?

    error: binding 'const double' to reference of type 'double&' discards qualifiers

    意思是:将const double 类型的数据 强行绑定到了 double 变量的引用上去,因为丢弃了限定符const而出错。

    由此可见,不能将const变量赋值给非const变量。

    本错误的修改方法1:将返回值类型改为const double&

    inline const double& real() const { return re;}

    修改方法2:将返回值类型修改为double (非引用)。

    inline double real() const { return re;}

    那么问题有来了,为什么能够返回引用和非引用两种方法?这就回到了返回引用和非引用的区别上来了:

    首先,这两种修改方法存在的前提是,成员函数为const变量。也就是说返回的成员变量是只读类型,即常量。

    那么,对于修改方法1。若返回引用,相当于直接可以在其他地方修改这个只读成员。显然出错,因此必须加上const

    对于方法2,因为只返回double类型,相当于复制了一份re的值返回了回去,这样对于只读成员是没有影响的。

    由此可见,两种方法都行得通。

    有时候不得不将成员函数设置为const,例如:

    const complex c1(2,1); 
    c1.real();

    它表示c1对象为常数的complex类型。

    若它调用非常成员函数:

    inline double real()  { return re;}

    则会报错:

    error: passing 'const complex' as 'this' argument discards qualifiers

    它的意思是将const complex类型的变量传递给this,实参丢失了const修饰符。这样可能造成修改常对象c1的隐患,因此const对象必须调用常成员函数。

     

    另外,只有成员函数才能被const修饰。所有在类外面定义的函数,都不能被const修饰。

    头铁不信系列:

    complex operator+(const complex& com1,const complex& com2) const
    {
        return complex(com1.real()+com2.real(),com1.image()+com2.image());
    }

    注意:此时我将类外的+重载函数一const修饰,立马便已出现错误:

    error: non-member function 'void fun(const complex&, const complex&)' cannot have cv-qualifier

    其中cv指的是const和vilatile关键字简写,cv-qualifier指的是const和vilatile限定符。

    错误信息的意思是:非成员函数 'void fun(const complex&, const complex&)' 不能有const和vilatile限定符。

     

     最后,附上传引用和传值(pass by value vs. pass by reference(to const))的区别:

      传值:将值全部传过去,值多大字节,那么就传多少字节。

      传引用:引用相当于指针。速度很快。若希望引用传过去后不希望它改,那么可以改成const

    返回值传递和引用传递:(return by value vs. Return by reference)

      若在函数内创建对象,则因为函数一结束,对象就被析构了,则不能够传引用。除了这种情况,都可以传引用。

      传送者无需知道接收者是否以reference形式接收。

      返回类型不能是void,因为使用者可能连续使用操作运算符。例如:c1+=c2+=c3

    当然要注意:引用&跟在类型后面,取地址放在变量前面。

    建议:尽量使用引用传递。

    四.内联函数:

    出现inline关键字的函数都是内联函数。内联函数是c++特色之一。目的是提高程序运行的速度,当然也有缺点。

    内联函数的用法是将inline关键字放在函数返回类型的最前端,例如:

    inline complex operator+(const complex& com1,const complex& com2)
    {
        return complex(com1.real()+com2.real(),com1.image()+com2.image());
    }

    内联函数的具体实现方法就是:在内联函数的调用处,在编译器编译期间,将内联函数的代码恰当地替换到该出。这样做的好处是,避免了程序运行时调用函数进行压栈,出栈等等繁琐的事情,省出了很多时间。但是相对的,缺点就是,每次调用处的一行代码变成了多行函数的实现代码,这样显然增加了源程序的代码量,使源程序占用的内存空间更大。

    使用内联函数的准则:

      内联函数里面不能出现循环语句和switch-case开关语句。

      在类里面定义并实现的成员函数默认是内联函数。

      在类里面声明,在类外面实现,只要出现了inline,就是内联函数

      在类里面声明,在类外面实现,若没有出现一个inline,就不是内联函数。

      inline关键字只是表示对编译器的建议。若不能够内联而强制写上inline,编译器并不会报错,只是它会忽略inline这个关键字而已。

     

    五.构造函数:

    构造函数是在对象被创建后,用来给数据成员初始化用的成员函数。其形式为:

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

    构造函数应该注意的几点:

      1.在对象创建后调用。

      2.构造函数名和类名相同。

      3.可以拥有参数。默认参数既可以在类中声明,也可以在定义处声明,但不能两处都声明~~~

      4.参数可以有默认值。

      5.没有返回值。

    强烈建议用上面初始化的形式,若是直接在括号里面赋值:

    complex(double r=0.0,double i=0.0)
    {
        re=r;
        im=i;
    }

    这样也行,但是缺点是放弃初始化,去进行赋值,这样效率会比较低。

    简单成员函数可以在类里直接定义,复杂一点的成员在在类里面声明,在类外实现。

     在调用构造函数是,若不传实参,则只需要写:

    string s1;//无参数的默认构造函数。

    写成以下形式就是错的:

    string s1();//它是对函数声明的格式,不是调用默认构造函数哦!!!

    六.运算符重载函数:

    起源:我们希望自定义的复数类对象能够通过+=,+,<<等等这些运算符直接进行赋值,加法或者输出等等,那么用运算符成员函数吧。在任何出现运算符的地方,它都可以直接被调用。其形式为:

    inline complex operator+(const double& dou,const complex& com1)
    {
        return complex(dou+com1.real(),com1.image());
    }
    
     ostream& operator<<(ostream &os,const complex& x)
    {
        os<<'('<<x.real()<<','<<x.image()<<')'<<endl;
    }

    当执行类似以下的语句时,运算符重载函数就会被自动调用:

    complex c1(2,1);
    complex c2(4);
    c2+=c1;

    这时调用的时+=运算符重载函数。+=会作用于c2对象。也就是说c2会调用 += 操作符重载函数,c2会传给this指针,c1会传给形参。注意,os不能设置为const,因为os流对象会在每次给它传值的时候它的状态会改变。另外,运算符重载函数放在类里面定义或在类外面定义都可以,主要看哪种行得通。在类外面定义更为通用,因为若是在类里面定义运算符重载函数,那么最左边的操作数一定要是complex类对象,这样显然局限性太大。在类里面定义重载函数,要省略最左边的complex形参。然而,在类外面定义的重载函数,不能省略调用重载函数的形参。

    关于重载函数,可以总结一些规律出来:

      c++允许出现多个函数名相同的函数。但是要求参数的个数或者参数的类型不同。这种出现多个同名函数的情况就是函数的重载。满足以上条件,经过编译器编译之后,这些同名函数实际上名字就不一样了。

    注意:构造函数重载时,若出现了默认值,则有可能报错哦~,例如:

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

    这两种构造函数的重载,若出现以下创建对象的形式:

    complex com;

    就会报错:

    error: call of overloaded 'complex()' is ambiguous

    note: candidate: complex::complex(double, double)

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

    也就是说,这两个构造函数是一样的,系统不知道该选择哪个构造函数进行初始化。其实将第二个构造函数删掉就解决问题了。第一个构造函数更通用一点。

    七.友元:

    在成员函数最前面加上friend 的函数就是友元。例如:

    friend complex& __doapl(complex& com1,const complex& com2);

    它的功能是,complex声明,complex类外面有一个函数叫__dopal 。__doapl这个函数是我的朋友,这个函数里面的complex对象可以访问它的私有变量。也就是是说,友元破坏了对象的封装性,强行允许类外的对象直接访问并修改其私有变量。

  • 相关阅读:
    js控制表格隔行变色
    浅谈css的伪元素::after和::before
    CSS 背景色变化 结构化伪类的练习
    css清除浮动的几种方式,哪种最合适?
    怎么去检测浏览器支不支持html5和css3?
    display:flex 布局详解(2)
    css3弹性盒子display:flex
    Referer和空Referer
    不要懒惰,坚持每周总结一篇博客
    比较2个文件的不同处
  • 原文地址:https://www.cnblogs.com/yulianggo/p/9251290.html
Copyright © 2020-2023  润新知