c++ Primer 5 12.1.6节练习 (头文件相互包含的问题及解决办法)

来源:互联网 发布:mysql update 索引 编辑:程序博客网 时间:2024/06/14 13:05

12.19

拿此题来说,StrBlobPtr类要用到StrBlob类,而StrBlob类要用到StrBlobPtr类。

即如下:

class StrBlob{friend class StrBlobPtr;public:typedef std::vector<std::string>::size_type size_type;StrBlob();//默认构造函数StrBlob(std::initializer_list<std::string>);//接收1个参数的initializer_list作为形参size_type size() const { return data->size(); }bool empty() const { return data->empty(); }void push_back(const std::string &t) { return data->push_back(t); }void pop_back();std::string& front();std::string& back();StrBlobPtr begin() { return StrBlobPtr(*this); }//返回一个指向首元素的迭代器StrBlobPtr end(){ auto ret = StrBlobPtr(*this, this->size()); return ret; }//返回尾后的迭代器private:std::shared_ptr<std::vector<std::string>> data;//一个指向vector<string>的智能指针void check(size_type i, const std::string &msg) const;//检查data[i]是否合法,抛出异常};

class StrBlobPtr{public:StrBlobPtr() :curr(0) { }//默认构造函数,令curr为0StrBlobPtr(StrBlob &a, size_t t = 0) :wptr(a.data), curr(t)  { }//带参数的构造函数string & deref() const;//解引用操作StrBlobPtr & incr();//递增操作private:shared_ptr<vector<string>> check(size_t, const string&) const;//检查下标是否过界weak_ptr<vector<string>> wptr;//定义一个空的weakptrsize_t curr;//定义元素下标};

可以看到这两个类要相互包含,那么该怎样处理?

1.如果两个都在各自的.h中,并且相互包含对方的.h,不用前置声明。

即在StrBlob.h文件中 #include "StrBlobPtr.h" 在StrBlobPtr.h文件中 #include "StrBlob.h"

在main.cpp中 #include "StrBlob.h" ,会出现什么样的错误呢?

error C2027: 使用了未定义类型“StrBlobPtr”,即该类未定义。
因为在头文件中使用了保护符,那么编译器只对每个头文件编译一次,那么在StrBlob.h中就不再编译StrBlobPtr类,即StrBlob使用的是未定义的类。


2.如果对1进行前置声明 ,为了清晰明了,用类A和B表示。

 #pragma once #include "B.h" class B;  class A { public:         B* b; };  //文件B.h中的代码 #pragma once #include "A.h" class B;  class B { public:         A* a; };  
这样,我们发现编译成功。即说明了,头文件不能替代前置声明。

但我们将各自对方的头文件去掉,再次编译,发现仍然编译成功。那么什么时候该包含对方的.h文件呢?


3.包含对方.h头文件的原则。

/***********************************此段来自网络*********************************/

头文件包含其实是一想很烦琐的工作,不但我们看着累,编译器编译的时候也很累,再加上头文件中常常出现的宏定义。感觉各种宏定义的展开是非常耗时间的,远不如自定义函数来得速度。这里仅就不同头文件、源文件间的句则结构问题提出两点原则,仅供参考:
    第一个原则应该是,如果可以不包含头文件,那就不要包含了。这时候前置声明可以解决问题。如果使用的仅仅是一个类的指针,没有使用这个类的具体对象(非指针),也没有访问到类的具体成员,那么前置声明就可以了。因为指针这一数据类型的大小是特定的,编译器可以获知。
    第二个原则应该是,尽量在CPP文件中包含头文件,而非在头文件中。假设类A的一个成员是是一个指向类B的指针,在类A的头文件中使用了类B的前置声明并编译成功,那么在A的实现中我们需要访问B的具体成员,因此需要包含头文件,那么我们应该在类A的实现部分(CPP文件)包含类B的头文件而非声明部分(H文件)。

/****************************************************************************************/

当我们的类只用到另一个类的的指针,引用等,没有去使用这个类的实例和他的具体成员,我们就直接使用前置声明,无需在自己的头文件.h中去包含对方的的.h,包含对方的.h可以放在自己的.cpp文件中去。


4.如果我们必须要使用另一个类的具体实例,或者成员函数。该怎么做?

一、使用全局变量+extern声明。

但实际上这样也破坏封装性。


二、使用基类指针。

...............之后复习基类在补充。


三、将两个类写在一个.h文件中,不要忘记前置声明。

按此题为例:

class StrBlobPtr;class StrBlob{friend class StrBlobPtr;public:typedef vector<string>::size_type size_type;StrBlob();//默认构造函数StrBlob(initializer_list<string>);//接收1个参数的initializer_list作为形参size_type size() const { return data->size(); }bool empty() const { return data->empty(); }void push_back(const string &t) { return data->push_back(t); }void pop_back();string& front();string& back();//StrBlobPtr begin() { return StrBlobPtr(*this); }//返回一个指向首元素的迭代器//StrBlobPtr end(){ auto ret = StrBlobPtr(*this, this->size()); return ret; }//返回尾后的迭代器StrBlobPtr begin();//注意,这两个函数必须在类StrBlobPtr定义后,才能定义,因为它使用了StrBlobPtr的具体实例StrBlobPtr end();private:shared_ptr<vector<string>> data;//一个指向vector<string>的智能指针void check(size_type i, const string &msg) const;//检查data[i]是否合法,抛出异常};class StrBlobPtr{public:StrBlobPtr() :curr(0) { }//默认构造函数,令curr为0StrBlobPtr(StrBlob &a, size_t t = 0) :wptr(a.data), curr(t)  { }//带参数的构造函数string & deref() const;//解引用操作StrBlobPtr & incr();//递增操作private:shared_ptr<vector<string>> check(size_t, const string&) const;//检查下标是否过界weak_ptr<vector<string>> wptr;//定义一个空的weakptrsize_t curr;//定义元素下标};

然后在.cpp中定义各个函数即可。

 值得注意的是,写在一个.h文件中时,前置声明之后,那个类是不完全的类,我们同样只能使用他的指针或者引用等,如红色的两行只能先声明,在StrBlobPtr定义之后在定义。


四、如果用到对方类,只用其类的指针或者引用,然后在自己的.h里前置声明对方类,不用包含对方的.h,在自己的.cpp实现时,再包含对方的.h

如下:

StrBlob.h中:

#ifndef STRBLOB_H_#define STRBLOB_H_#include <iostream>#include <vector>#include <initializer_list>#include <memory>#include <stdexcept>using namespace std;class StrBlobPtr;class StrBlob{

StrBlob.cpp中

#include "StrBlob.h"#include "StrBlobPtr.h"

StrBlobPtr.h中

#ifndef STRBLOBPTR_H_#define STRBLOBPTR_H_#include <iostream>#include <vector>#include <initializer_list>#include <memory>#include <stdexcept>using namespace std;class StrBlob;class StrBlobPtr{

StrBlobPtr.cpp中

#include "StrBlobPtr.h"#include "StrBlob.h"

这样,程序可以正常编译。


12.20

#include <iostream>#include <fstream>#include <string>#include <vector>#include "StrBlob.h"#include "StrBlobPtr.h"using namespace std;int main(){    ifstream input("F:\\test\\input.txt");    string temp;    StrBlob sb;    if (getline(input, temp))    {        sb.push_back(temp);        while (getline(input, temp))        {            sb.push_back(temp);        }    }    else        cerr << "No data!" << endl;    for (auto i = sb.begin(); neq(i, sb.end()); i.incr()) //neq即!= ,incr()即++i        cout << i.deref() << endl;    system("pause");    return 0;}

可以得到:



原创粉丝点击