zoukankan      html  css  js  c++  java
  • C++基础学习教程(六)----类编写的前情回想以及项目实战(1)

    在開始类的编写之前我们依旧须要回想整理一下前面所说的内容,(前面尽管是一个自己定义数据类型的实现过程,可是内容有点繁杂).

    先看一段代码:

    /** @file calssStruct.cpp */
    /** Member Functions for Class point */
    #include <cmath> // for sqrt and atan
    
    using namespace  std;
    
    struct point
    {
      point()
      : x_(0.0), y_(0.0)
      {}
      point(double x, double y)
      : x_(x), y_(y)
      {}
      point(point const& pt)
      : x_(pt.x_), y_(pt.y_)
      {}
    
      /// Distance to the origin.
      double distance()
      {
        return std::sqrt(x*x + y*y);
      }
      /// Angle relative to x-axis.
      double angle()
      {
        return std::atan2(y, x);
      }
    
      /// Add an offset to x and y.
      void offset(double off)
      {
        offset(off, off);
      }
      /// Add an offset to x and an offset to y
      void offset(double  xoff, double yoff)
      {
        x = x + xoff;
        y = y + yoff;
      }
    
      /// Scale x and y.
      void scale(double mult)
      {
        this->scale(mult, mult);
      }
      /// Scale x and y.
      void scale(double xmult, double ymult)
      {
        this->x = this->x * xmult;
        this->y = this->y * ymult;
      }
      double x_;
      double y_;
    };
    

    上面代码中有构造函数,有成员函数,有数据成员.我们如今须要分析一个在上面和之前的成员函数中出现的一个this引用.

    在C++中,每一个成员函数都有一个隐含的形參this,当成员函数被调用时,其对象本身就被编译器作为实參隐式传入.在成员函数中能够使用*this表达式訪问该对象.在C++语法中点操作符比星操作符优先级高,因此须要使用圆括号(如(*this).x).还有一种等价的调用时使用”箭头”,即是this->x.

    可是事实上编译器能够自己主动识别那些是成员名字,所以this->是可选的,即是能够省略的.可是有时候为了代码清晰总是加上它.而一些程序猿则为了表明是数据成员,有时候往往在数据成员名称前面加上前缀,或者在后面加上后缀.我倾向于后缀,如上面的代码.

    关于构造函数,初始化时类和内置类型的一个重要差别,假设定义一个内置类型而不提供初始化序列,那么它的值就是无意义的;而定义类的对象时,该对象一定会被构造器初始化,因此总有机会初始化数据成员.

    虽然构造函数的冒号后面的初始化列表是可选的,可是建议总是加上.

    依据数据成员的类型是类或者内置类型,编译器会做出不同的处理.每一个成员的初始化表有以下的三种情况:


    以下再来一个构造函数的验证代码:

    /** @file constructFun.cpp */
    /** Visual Constructors */
    #include <iostream>
    #include <ostream>
    
    using namespace std;
    
    struct demo
    {
      demo()      : x_(0) { std::cout << "default constructor
    "; }
      demo(int x) : x_(x) { std::cout << "constructor(" << x << ")
    "; }
      demo(demo const& that)
      : x_(that.x_)
      {
        std::cout << "copy constructor(" << x_ << ")
    ";
      }
      int x_;
    };
    
    demo addone(demo d)
    {
      ++d.x_;
      return d;
    }
    
    int main()
    {
      demo d1;
      demo d2(d1);
      demo d3(42);
      demo d4(addone(d3));
    }
    

    结果例如以下:

    可是是否是学习到这里我们就把构造函数什么的搞明确了呢,以下我们来測试一下,先看代码:

    /** @file Test_Construct.cpp */
    /** Mystery Program */
    #include <iostream>
    #include <ostream>
    
    using namespace std;
    struct point
    {
      point()
      : x_(0.0), y_(0.0)
      {
        cout << "default constructor
    ";
      }
      point(double x, double y)
      : x_(x), y_(y)
      {
        cout << "constructor(" << x << ", " << y << ")
    ";
      }
    
      double x_;
      double y_;
    };
    
    int main()
    {
      point pt();
    }
    

    你觉得这个编译后会输出什么呢?是default constructor么?

    可是你错了……这就是输出:

    即是根本未定义变量,编译器觉得你编写了一个无參数的返回值为point的名字是pt的函数声明!

    假设你不理解那么把point改成int,如今是intpt();这样是不是就更像一个函数声明了呢.可是假设要定义一个变量应该怎么样的呢?

    是 point pt;即是不带括号这个时候调用了默认的无參数构造函数初始化.

    所以在定义变量的时候注意哪些括号是必须的,不然非常可能就会误导编译器将你的所谓的”变量声明”当成函数声明.


    练习项目一

    身体质量指数BMI小程序

    学习了那么多能够做个小项目练习一下了.要计算BMI须要知道一个人的身高体重,BMI的计算公式是体重/(身高^2),结果是一个无单位的值.如今的任务是编写一个程序,使其能够读取记录,打印记录并计算一些统计值.该程序以请求一个BMI上极限開始,仅打印BMI值大雨或等于此极限值的记录,每条记录包括姓名(能够有空格),体重,身高(单位cm),以及性别(M或F,不限大写和小写).读取完每一个人的记录后要马上打印该记录的BMI值,手机全部的记录后,基于数据打印两个表------男性一个,女性一个.在BMI值后面用*标记超过界限的BMI记录.并打印BMI的均值和中值.

    当中的一个演示样例代码例如以下.

    /** @file BMI_Again.cpp */
    /** New BMI Program */
    #include <algorithm>
    #include <cstdlib>
    #include <iomanip>
    #include <ios>
    #include <iostream>
    #include <istream>
    #include <limits>
    #include <locale>
    #include <ostream>
    #include <string>
    #include <vector>
    
    using namespace std;
    
    /// Compute body-mass index from height in centimeters and weight in kilograms.
    int compute_bmi(int height, int weight)
    {
       return static_cast<int>(weight * 10000 / (height * height) + 0.5);
    }
    
    /// Skip the rest of the input line.
    void skip_line(istream& in)
    {
      in.ignore(numeric_limits<int>::max(), '
    ');
    }
    
    /// Represent one person’s record, storing the person’s name, height, weight,
    /// sex, and body-mass index (BMI), which is computed from the height and weight.
    struct record
    {
      record() : height_(0), weight_(0), bmi_(0), sex_('?'), name_()
      {}
    
      /// Get this record, overwriting the data members.
      /// Error-checking omitted for brevity.
      /// @return true for success or false for eof or input failure
      bool read(istream& in, int num)
      {
        cout << "Name " << num << ": ";
        string name;
        if (not getline(in, name))
          return false;
    
        cout << "Height (cm): ";
        int height;
        if (not (in >> height))
          return false;
        skip_line(in);
    
        cout << "Weight (kg): ";
        int weight;
        if (not (in >> weight))
          return false;
        skip_line(in);
    
        cout << "Sex (M or F): ";
        char sex;
        if (not (in >> sex))
          return false;
        skip_line(in);
        sex = toupper(sex, locale());
    
        // Store information into data members only after reading
        // everything successfully.
        name_ = name;
        height_ = height;
        weight_ = weight;
        sex_ = sex;
        bmi_ = compute_bmi(height_, weight_);
        return true;
      }
    
      /// Print this record to @p out.
      void print(ostream& out, int threshold)
      {
        out << setw(6) << height_
            << setw(7) << weight_
            << setw(3) << sex_
            << setw(6) << bmi_;
        if (bmi_ >= threshold)
          out << '*';
        else
          out << ' ';
        out << ' ' << name_ << '
    ';
      }
    
      int height_;       ///< height in centimeters
      int weight_;       ///< weight in kilograms
      int bmi_;          ///< Body-mass index
      char sex_;         ///< 'M' for male or 'F' for female
      string name_; ///< Person’s name
    };
    
    
    /** Print a table.
     * Print a table of height, weight, sex, BMI, and name.
     * Print only records for which sex matches @p sex.
     * At the end of each table, print the mean and median BMI.
     */
    void print_table(char sex, vector<record>& records, int threshold)
    {
      cout << "Ht(cm) Wt(kg) Sex  BMI  Name
    ";
    
      float bmi_sum(0);
      long int bmi_count(0);
      vector<int> tmpbmis; // store only the BMIs that are printed
                                // in order to compute the median
      for (vector<record>::iterator iter(records.begin());
           iter != records.end();
           ++iter)
      {
        if (iter->sex_ == sex)
        {
          bmi_sum = bmi_sum + iter->bmi_;
          ++bmi_count;
          tmpbmis.push_back(iter->bmi_);
          iter->print(cout, threshold);
        }
      }
    
      // If the vectors are not empty, print basic statistics.
      if (bmi_count != 0)
      {
        cout << "Mean BMI = "
                  << setprecision(1) << fixed << bmi_sum / bmi_count
                  << '
    ';
    
        // Median BMI is trickier. The easy way is to sort the
        // vector and pick out the middle item or items.
        sort(tmpbmis.begin(), tmpbmis.end());
        cout << "Median BMI = ";
        // Index of median item.
        int i(tmpbmis.size() / 2);
        if (tmpbmis.size() % 2 == 0)
          cout << (tmpbmis.at(i) + tmpbmis.at(i-1)) / 2.0 << '
    ';
        else
          cout << tmpbmis.at(i) << '
    ';
      }
    }
    
    /** Main program to compute BMI. */
    int main()
    {
      locale::global(locale(""));
      cout.imbue(locale());
      cin.imbue(locale());
    
      vector<record> records;
      int threshold;
    
      cout << "Enter threshold BMI: ";
      if (not (cin >> threshold))
        return EXIT_FAILURE;
      skip_line(cin);
    
      cout << "Enter name, height (in cm),"
                   " and weight (in kg) for each person:
    ";
      record rec;
      while (rec.read(cin, records.size()+1))
      {
        records.push_back(rec);
        cout << "BMI = " << rec.bmi_ << '
    ';
      }
    
      // Print the data.
      cout << "
    
    Male data
    ";
      print_table('M', records, threshold);
      cout << "
    Female data
    ";
      print_table('F', records, threshold);
    }
    

    执行结果例如以下:

    可是看到上面的这个函数:

    你是否认为函数參数传递应该是const类型更合适呢,我们改动了一下,測试,发现,,,不行,出现了错误.可是显然应该是const类型的引用传递.那怎么实现呢.

    应该记得在每一个成员函数的内部都有一个this隐藏參数,上面的代码段中,print_table调用print成员函数,可是经过改动,this引用了一个const对象.虽然你知道print函数不会改动不论什么的数据成员,可是编译器不知道,因此你须要让编译器知道,怎么做呢,仅仅要在函数头和函数体之间加上一个const修饰符就可以.例如以下:

    而一个通用的规则是,为全部的不改动成员变量的函数加入const修饰符,它确保程序在有const对象的时候就能够调用成员函数.


  • 相关阅读:
    CF1187E Tree Painting
    [TJOI2017]城市
    [HNOI2010]合唱队
    2020暑假多校补题记录
    树形dp总结
    2017CCPC 秦皇岛 G. Numbers (贪心 + java大数)
    LOJ 2491 求和 (LCA + 前缀和)
    LOJ 10105. 欧拉回路
    Luogu P3953 逛公园 (最短路+dp)
    LOJ#2718. 「NOI2018」归程 (kruskal重构树)
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/5360707.html
Copyright © 2011-2022 走看看