zoukankan      html  css  js  c++  java
  • C++Primer 5th 练习 12.19

      这阵子真是太忙了, 连续做了四个课设。 当然这并不能作为好久没写博客的借口, 没写博客的主要原因只有一个: 。 最近又开始回顾C++的语法与特性(据说C++就是一门需要反复回顾的语言),以及学习C++的编程规范。 敲了C++Primer 5th 上的一道典型的练习题,纪念一下这即将过去的2016.

      题目描述: 定义你自己版本的 StrBlobPtr, 更新 StrBlob类, 加入恰当的 friend 声明及begin 和 end 成员。

      这道题目主要是练习 智能指针 share_ptrweak_ptr

      我的环境: Win10 + VS2015

      声明 StrBlob 类和 类StrBlobPtr的文件: StrBlob.h  

     1 #pragma once
     2 #ifndef PRACTICE_STRBLOB_H_
     3 #define PRACTICE_STRBLOB_H_
     4 #include <memory>
     5 #include <vector>
     6 #include <string>
     7 #endif  PRACTICE_STRBLOB_H_
     8 
     9 // 对于 StrBlob 中的友元声明来说,这个前置声明是必要的
    10 class StrBlobPtr; 
    11 class StrBlob {
    12     friend class StrBlobPtr;
    13 public:
    14     typedef std::vector<std::string>::size_type size_type; 
    15     StrBlob():data(std::make_shared<std::vector<std::string>>()) { }
    16     StrBlob(std::initializer_list<std::string>il):data(std::make_shared<std::vector<std::string>>(il)){ }
    17     size_type size() const { return data->size();  }
    18     bool empty() const { return data->empty();  }
    19     // 添加和删除元素
    20     void push_back(const std::string &t) { data->push_back(t);  }
    21     void pop_back(); 
    22     // 元素访问
    23     std::string& front(); 
    24     std::string& back(); 
    25     const std::string& front() const; 
    26     const std::string& back() const;
    27 
    28     // 返回指向首元素和尾元素的 StrBlobPtr 
    29     StrBlobPtr begin();
    30     StrBlobPtr end();
    31 private:
    32     std::shared_ptr<std::vector<std::string>> data; 
    33     void check(size_type i, const std::string &msg) const; 
    34 };
    35 
    36 
    37 // 对于访问一个不存在元素的尝试, StrBlobPtr抛出一个异常
    38 class StrBlobPtr {
    39 public:
    40     StrBlobPtr(): curr(0) { }
    41     StrBlobPtr(StrBlob &a, size_t sz = 0): 
    42         wptr(a.data), curr(sz) { }
    43     std::string& deref() const; 
    44     StrBlobPtr& incr();        // 前缀递增
    45 private:
    46     // 若检查成功, check返回一个指向 vector 的 shared_ptr 
    47     std::shared_ptr<std::vector<std::string>>
    48         check(std::size_t i, const std::string& msg) const; 
    49     // 保存一个 weak_ptr, 意味着底层 vector 可能会被销毁
    50     std::weak_ptr<std::vector<std::string>> wptr; 
    51     std::size_t curr;        // 在数组中的当前位置
    52 };
    53 
    54 
    55 
    56  

      实现 StrBlob 类和 类StrBlobPtr的文件: StrBlob.cpp

     1 #include "stdafx.h"
     2 #include "StrBlob.h"
     3 
     4 //-----------------------------------
     5 //        类 StrBlob 的实现
     6 //-----------------------------------
     7 void StrBlob::pop_back()
     8 {
     9     check(0, "pop_back on empty StrBlob"); 
    10     data->pop_back(); 
    11 }
    12 
    13 std::string& StrBlob::front()
    14 {
    15     // 如果 vector 为空, check 会抛出一个异常
    16     check(0, "front on empty StrBlob"); 
    17     return data->front(); 
    18 }
    19 
    20 std::string& StrBlob::back()
    21 {
    22     // 如果 vector 为空, check 会抛出一个异常
    23     check(0, "back on empty StrBlob"); 
    24     return data->back(); 
    25 }
    26 
    27 const std::string& StrBlob::front() const
    28 {
    29     check(0, "front on empty StrBlob"); 
    30     return data->front(); 
    31 }
    32 
    33 const std::string& StrBlob::back() const
    34 {
    35     check(0, "back on empty StrBlob");
    36     return data->back(); 
    37 }
    38 
    39 void StrBlob::check(size_type i, const std::string& msg) const
    40 {
    41     if (i >= data->size())
    42         throw std::out_of_range(msg); 
    43 }
    44 
    45 //    必须在 StrBlobPtr 定义之后进行定义
    46 StrBlobPtr StrBlob::begin()
    47 {
    48     return StrBlobPtr(*this);
    49 }
    50 
    51 // 必须在 StrBlobPtr 定义之后进行定义
    52 StrBlobPtr StrBlob::end()
    53 {
    54     auto ret = StrBlobPtr(*this, data->size());
    55     return ret;
    56 }
    57 
    58 
    59 //----------------------------------------
    60 //         类 StrBlobPtr 的实现 
    61 //----------------------------------------
    62 
    63 std::string & StrBlobPtr::deref() const
    64 {
    65     auto p = check(curr, "dereferemce past end");
    66     return (*p)[curr];    // (*p) 是对象所指向的 vector 
    67 }
    68 
    69 StrBlobPtr & StrBlobPtr::incr()
    70 {
    71     // 如果 curr 已经指向容器的尾后位置, 就不能递增它
    72     check(curr, "increment past end of StrBlobPtr"); 
    73     ++curr;        // 推进当前位置
    74     return *this; 
    75 }
    76 
    77 std::shared_ptr<std::vector<std::string>> StrBlobPtr::check(std::size_t i, const std::string & msg) const
    78 {
    79     auto ret = wptr.lock();            // 检查 vector 是否还存在
    80     if (!ret)
    81         throw std::runtime_error("unbound StrBlobPtr"); 
    82     if (i >= ret->size())
    83         throw std::out_of_range(msg); 
    84 
    85     return ret;        // 否则, 返回指向 vector 的 shared_ptr 
    86 }

      用于测试的文件: practice_12.19.cpp

     1 // practice_12.19.cpp : 定义控制台应用程序的入口点。
     2 //
     3 
     4 #include "stdafx.h"
     5 #include "StrBlob.h"
     6 #include <iostream>
     7 using namespace std; 
     8 
     9 int main()
    10 {
    11     StrBlob blob{ "So", "Fun", "So", "Good" }; 
    12     StrBlobPtr blobPtr(blob); 
    13     cout << blob.front() << endl;
    14     blobPtr.incr(); 
    15     cout << blobPtr.deref() << endl; 
    16     blobPtr = blob.begin(); 
    17     cout << blobPtr.deref() << endl; 
    18     cout << blob.back() << endl; 
    19     return 0;
    20 }

    运行结果截图:

      这道题目是很经典的,但是很不幸, C++Primer 5th (中文版)上,却把这道题目的代码写错了一丢丢。

    回顾一下这道题涉及的主要姿势:

    一般程序使用动态内存的原因

    1. 程序不知道自己需要使用多少对象
    2. 程序不知道所需对象的准确类型
    3. 程序需要在多个对象间共享数据(这个例子就是共享数据的问题)

    友元的相关知识

    1. 友元类一定要事先声明(或定义)
    2. 需要用到友元类中的具体成员时,必须保证友元类已经定义。

    列表初始化

    头文件定义规范

    google 的 C++ 编码规范之头文件

     头文件
    通常,每一个.cc 文件(C++的源文件)都有一个对应的.h 文件(头文件) ,也有一些例外,如单元测试代
    码和只包含main()的.cc 文件。
    正确使用头文件可令代码在可读性、文件大小和性能上大为改观。
    下面的规则将引导你避免使用头文件时的各种麻烦。
    1. #define 保护
    所有头文件都应该使用#define 防止头文件被多重包含(multiple inclusion) ,命名格式为:
    <PROJECT>_<PATH>_<FILE>_H_
    为保证唯一性,头文件的命名应基于其所在项目源代码树的全路径。例如,项目 foo 中的头文件
    foo/src/bar/baz.h 按如下方式保护:
    #ifndef FOO_BAR_BAZ_H_
    #define FOO_BAR_BAZ_H_
    ...
    #endif // FOO_BAR_BAZ_H_
    2. 头文件依赖
    使用前置声明(forward declarations)尽量减少.h 文件中#include 的数量。
    当一个头文件被包含的同时也引入了一项新的依赖(dependency) ,叧要该头文件被修改,代码就要重新
    编译。如果你的头文件包吨了其他头文件,这些头文件的任何改变也将导致那些包含了你的头文件的代码
    重新编译。因此,我们应该尽量少的包含头文件,尤其是那些包含在其他头文件中的。
    使用前置声明可以显著减少需要包含的头文件数量。举例说明:头文件中用到类 File,但不需要访问 File
    的声明,则头文件中只需前置声明 class File;无需#include "file/base/file.h"。
    在头文件如何做到使用类 Foo 而无需访问类的定义?
    1) 将数据成员类型声明为 Foo *或 Foo &;
    2) 参数、返回值类型为 Foo 的函数只是声明(但不定义实现) ;
    3) 静态数据成员的类型可以被声明为 Foo,因为静态数据成员的定义在类定义之外。
    另一方面,如果你的类是 Foo 的子类,或者含有类型为 Foo 的非静态数据成员,则必须为之包含头文件。
    有时,使用指针成员(pointer members,如果是 scoped_ptr 更好)替代对象成员(object members)
    的确更有意义。然而,返样的做法会降低代码可读性及执行效率。如果仅仅为了少包含头文件,还时不要
    这样替代的好。
    当然,.cc 文件无论如何都需要所使用类的定义部分,自然也就会包含若干头文件。
    注:能依赖声明的就不要依赖定义。

  • 相关阅读:
    Spring(四)
    安卓学习25(BaseAdapter优化)
    安卓学习24(Adapter)
    每周总结(4.4)
    安卓学习23(Date & Time组件)
    安卓学习22(Date & Time组件)
    安卓学习21(ScrollView(滚动条))
    安卓学习20(RatingBar(星级评分条))
    每周总结(3.28)
    安卓学习19(SeekBar(拖动条))
  • 原文地址:https://www.cnblogs.com/acm1314/p/6240195.html
Copyright © 2011-2022 走看看