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

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

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

  我的环境: Win10 + VS2015

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

 #pragma once
#ifndef PRACTICE_STRBLOB_H_
#define PRACTICE_STRBLOB_H_
#include <memory>
#include <vector>
#include <string>
#endif PRACTICE_STRBLOB_H_ // 对于 StrBlob 中的友元声明来说,这个前置声明是必要的
class StrBlobPtr;
class StrBlob {
friend class StrBlobPtr;
public:
typedef std::vector<std::string>::size_type size_type;
StrBlob():data(std::make_shared<std::vector<std::string>>()) { }
StrBlob(std::initializer_list<std::string>il):data(std::make_shared<std::vector<std::string>>(il)){ }
size_type size() const { return data->size(); }
bool empty() const { return data->empty(); }
// 添加和删除元素
void push_back(const std::string &t) { data->push_back(t); }
void pop_back();
// 元素访问
std::string& front();
std::string& back();
const std::string& front() const;
const std::string& back() const; // 返回指向首元素和尾元素的 StrBlobPtr
StrBlobPtr begin();
StrBlobPtr end();
private:
std::shared_ptr<std::vector<std::string>> data;
void check(size_type i, const std::string &msg) const;
}; // 对于访问一个不存在元素的尝试, StrBlobPtr抛出一个异常
class StrBlobPtr {
public:
StrBlobPtr(): curr() { }
StrBlobPtr(StrBlob &a, size_t sz = ):
wptr(a.data), curr(sz) { }
std::string& deref() const;
StrBlobPtr& incr(); // 前缀递增
private:
// 若检查成功, check返回一个指向 vector 的 shared_ptr
std::shared_ptr<std::vector<std::string>>
check(std::size_t i, const std::string& msg) const;
// 保存一个 weak_ptr, 意味着底层 vector 可能会被销毁
std::weak_ptr<std::vector<std::string>> wptr;
std::size_t curr; // 在数组中的当前位置
};

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

 #include "stdafx.h"
#include "StrBlob.h" //-----------------------------------
// 类 StrBlob 的实现
//-----------------------------------
void StrBlob::pop_back()
{
check(, "pop_back on empty StrBlob");
data->pop_back();
} std::string& StrBlob::front()
{
// 如果 vector 为空, check 会抛出一个异常
check(, "front on empty StrBlob");
return data->front();
} std::string& StrBlob::back()
{
// 如果 vector 为空, check 会抛出一个异常
check(, "back on empty StrBlob");
return data->back();
} const std::string& StrBlob::front() const
{
check(, "front on empty StrBlob");
return data->front();
} const std::string& StrBlob::back() const
{
check(, "back on empty StrBlob");
return data->back();
} void StrBlob::check(size_type i, const std::string& msg) const
{
if (i >= data->size())
throw std::out_of_range(msg);
} // 必须在 StrBlobPtr 定义之后进行定义
StrBlobPtr StrBlob::begin()
{
return StrBlobPtr(*this);
} // 必须在 StrBlobPtr 定义之后进行定义
StrBlobPtr StrBlob::end()
{
auto ret = StrBlobPtr(*this, data->size());
return ret;
} //----------------------------------------
// 类 StrBlobPtr 的实现
//---------------------------------------- std::string & StrBlobPtr::deref() const
{
auto p = check(curr, "dereferemce past end");
return (*p)[curr]; // (*p) 是对象所指向的 vector
} StrBlobPtr & StrBlobPtr::incr()
{
// 如果 curr 已经指向容器的尾后位置, 就不能递增它
check(curr, "increment past end of StrBlobPtr");
++curr; // 推进当前位置
return *this;
} std::shared_ptr<std::vector<std::string>> StrBlobPtr::check(std::size_t i, const std::string & msg) const
{
auto ret = wptr.lock(); // 检查 vector 是否还存在
if (!ret)
throw std::runtime_error("unbound StrBlobPtr");
if (i >= ret->size())
throw std::out_of_range(msg); return ret; // 否则, 返回指向 vector 的 shared_ptr
}

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

 // practice_12.19.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include "StrBlob.h"
#include <iostream>
using namespace std; int main()
{
StrBlob blob{ "So", "Fun", "So", "Good" };
StrBlobPtr blobPtr(blob);
cout << blob.front() << endl;
blobPtr.incr();
cout << blobPtr.deref() << endl;
blobPtr = blob.begin();
cout << blobPtr.deref() << endl;
cout << blob.back() << endl;
return ;
}

运行结果截图:

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

C++Primer 5th 练习 12.19的更多相关文章

  1. C++ Primer 5th 第12章 动态内存

    练习12.1:在此代码的结尾,b1 和 b2 各包含多少个元素? StrBlob b1; { StrBlob b2 = {"a", "an", "th ...

  2. U3D笔记11:47 2016/11/30-15:15 2016/12/19

    11:47 2016/11/30Before you can load a level you have to add it to the list of levels used in the gam ...

  3. TypeError: datetime.datetime(2016, 9, 25, 21, 12, 19, 135649) is not JSON serializable解决办法

    1.一个简单的方法来修补json模块,这样序列将支持日期时间. import json import datetime json.JSONEncoder.default = lambda self, ...

  4. Daily Scrum 12.19

    Member Task on 12.19 Task on 12.20 仇栋民 请假 完成Task972 : 完成活动评分基础功能 康家华 完成 Task1004 : 百度map UI优化 完成Task ...

  5. python练习题,写一个方法 传进去列表和预期的value 求出所有变量得取值可能性(例如list为[1,2,3,4,5,6,12,19],value为20,结果是19+1==20只有一种可能性),要求时间复杂度为O(n)

    题目:(来自光荣之路老师)a+b==valuea+b+c=valuea+b+c+d==valuea+b+c+d+...=valuea和b....取值范围都在0-value写一个方法 传进去列表和预期得 ...

  6. TypeError: datetime.datetime(2016, 9, 25, 21, 12, 19, 135649) is not JSON serializable解决办法(json无法序列化对象的解决办法)

    1.一个简单的方法来修补json模块,这样序列将支持日期时间. import json import datetime json.JSONEncoder.default = lambda self, ...

  7. 【读书笔记】C++ primer 5th 从入门到自闭(一)

    这几天看了C++ primer 5th的一二章,有很多收获,但是有的地方因为翻译的问题也搞得理解起来颇为难受啊啊啊啊.尤其是const限定符,在C语言并没有这么多复杂的语法,在C++里面语法细节就多的 ...

  8. 12.19 file 与io流

    12.19 file与io流 File 1. 新建文件或者文件夹 新建文件 File F = new File(pathname:"路径+文件名");Boolean flag = ...

  9. 2021.12.19 eleveni的刷题记录

    2021.12.19 eleveni的刷题记录 0. 本次记录有意思的题 0.1 每个点恰好经过一次并且求最小时间 P2469 [SDOI2010]星际竞速 https://www.luogu.com ...

随机推荐

  1. mac系统terminal连接linux

    ssh user@hostname user是管理员账号 hostname是服务器ip

  2. java 日期转时间戳,时间戳转为日期

    package date; import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Dat ...

  3. Eclipse版本android 65535解决方案(原理等同android studio现在的分包方式)

    由于工作的需要看了下Eclipse下android65535的解决方案,查了好多文档,真心的发自内心的说一句请不要再拷贝别人的博客了,害人,真害人. 接下来我说下我的实现方式,首先说下65535的最可 ...

  4. iphone H5视频行内播放(禁止全屏播放)

    一般用户都知道,ios在网页点击视频播放时,视频会弹出全屏播放框. video标签的playsinline.webkit-playsinline标记根本就不会起作用. 还有传闻说对于没有声音的视频不会 ...

  5. java关键字 super 和 this

    简单粗暴的说就是: super: 是指父类,想要在子类方法中调用父类的实例变量或方法可以通过super 来访问 this:是指当前类,想要访问当前类的实例变量和方法可以使用this,同时可以省略

  6. spring MVC和hibernate的结合

    我们在没有用注解写spring配置文件的时候:会在spring配置文件中定义Dao层的bean,这样我们在service层中,写setDao方法,就可以直接通过接口调用Dao层.            ...

  7. JAVA中的异常及处理异常的方法

    异常 这是我老师的喜好:就是说一上来就拿一张图给大家看看,过过瘾-_- 这是一张: 异常分类图 来,这里还有一张带中文的常见异常截图!!! 1:先来说说什么是异常吧: 其实就是"阻止当前方法 ...

  8. quick-cocos2d-x :加入精灵背景

    最近几天都在学习 quick  找例子学习. 一直也没什么好的. 并且 还不会 lua .学习学习 突突突 官方文档上的打地鼠 都没了. 不知道为什么链接不好使. 好吧 那么今天就先做一个简单的 例子 ...

  9. Dynamics AX 2012 R2 AIF 内部异常 output session was auto-closes

    今天调用AIF出现异常,异常信息如下 This chanel can no longer be used to send message as the output session was auto- ...

  10. [转]SSAS没有注册类 (异常来自 HRESULT:0x80040154 (REGDB_E_CLASSNOTREG)) (Microsoft Visual Studio)的解决办法

    转自:http://www.cnblogs.com/xvqm00/archive/2011/07/15/2107338.html 打开SSAS 数据源视图浏览数据时,提示 没有注册类别 (异常来自 H ...