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 文件无论如何都需要所使用类的定义部分,自然也就会包含若干头文件。
    注:能依赖声明的就不要依赖定义。

  • 相关阅读:
    IXmlSerializable With WCFData Transfer in Service Contracts
    Difference Between XmlSerialization and BinarySerialization
    Using XmlSerializer (using Attributes like XmlElement , XmlAttribute etc ) Data Transfer in Service Contracts
    Introducing XML Serialization
    Version Tolerant Serialization
    Which binding is bestWCF Bindings
    Data Transfer in Service Contracts
    DataContract KnownTypeData Transfer in Service Contracts
    Using the Message ClassData Transfer in Service Contracts
    DataContract POCO SupportData Transfer in Service Contracts
  • 原文地址:https://www.cnblogs.com/acm1314/p/6240195.html
Copyright © 2011-2022 走看看