65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Boost 设计健壮的对象

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (30投票s)

2004 年 8 月 17 日

11分钟阅读

viewsIcon

123581

downloadIcon

728

关于使用 Boost 库设计类的教程

引言

本教程将讨论一个健壮、可重用的类的设计。我们将设计接口、编写单元测试,并使用 Boost Operators 库来减少我们必须编写的代码量。

我们将要解决的具体案例是版本号处理类。为 Windows 编写的应用程序通常使用 Major.Minor.Build.Revision 格式。所以我们要设计一个可以操作这些版本号的类。

本教程使用了一些 Boost 库。Boost 是一个免费的、经过同行评审的、可移植的 C++ 源库集合。如果您从未接触过 Boost,您将大开眼界。本文档中,我们只会介绍 Operators 和 Test 库。我希望当您看到它们的一些强大功能时,您会受到启发,考虑如何在您的项目中使用其他库。

背景

作为一名顾问,我看到过大量的代码。我见过许多类只为满足特定需求而设计,而几乎不费吹灰之力,就可以创建一个完整的类并将其放入库中以供重用。拥有一个正确支持复制、赋值和比较的类,可以极大地方便在 STL 集合中使用该类。

我还为 FAA 和其他机构做过工作,这些工作需要完整的测试套件,从单元测试开始。在商业世界中,我很少看到有协调一致的单元测试的意识。通常只有团队中的一两个程序员会以临时的方式进行测试,因为他们有这样的纪律。拥有一套随代码一起维护的单元测试,为代码的维护和后期的重构(无论是为了性能还是可重用性)提供了坚实的基础。

确定需求

现在,让我们开始列出我们希望 AppVersion 类能够执行的操作。

  • 各种构造方法,即默认构造函数、带有版本指定的构造函数以及复制构造函数
  • 赋值
  • 各种访问器
  • 一套完整的比较运算符
  • 序列化
  • 调试支持

创建项目结构

在本文中,我们的解决方案将包含两个项目。一个是包含我们的 AppVersion 类的库,另一个是单元测试项目。您可以很容易地想象,我们的库可以包含大量类,而我们的单元测试项目将包含每个类的测试。我们将设置我们的项目以支持在未来的文章中添加更多类。通过将测试放在单独的项目中,它们不会给库的用户带来不必要的负担,但允许测试与库的源代码一起维护。

我们的目录结构将如下所示

Core
   |
   +--Core
   +--Tests

我们在此项目中使用的是Visual Studio 2003。但我们所做的一切在Visual Studio 6中都将有效。本文的源代码包含 VS7 解决方案和 VS6 工作区。

创建库

我们将首先创建一个名为 Core 的库(在向导的“Visual C++ Projects”下使用“Win32 Project”),并选择“Create directory for Solution”。务必选择“Static Library”和“MFC support”。

由于我们将类设计用于 MFC 项目,因此我们将从 CObject 派生它。这为我们提供了调试和序列化支持等额外优势。我们还将添加一些成员变量来保存版本号的四个部分。

class AppVersion : public CObject
{
    public :
        AppVersion();
        virtual ~AppVersion();

    private :
        unsigned long m_Major;
        unsigned long m_Minor;
        unsigned long m_Build;
        unsigned long m_Revision;
};

设置测试环境

为了遵循最佳实践,我们将首先编写单元测试。让我们开始设置测试环境。我们将使用 Boost Test Library。网上有很多单元测试库。我们将使用 Boost,因为我们稍后还将使用 Boost 库的其他功能。从 https://boost.ac.cn 下载最新版本,并将目录添加到 Visual Studio 的 include 路径中。测试库是通过模板实现的。我们只需要提供几个函数,模板就能完成其余的工作。因此,我们在解决方案中添加另一个名为 Tests 的项目,它只是一个具有 MFC 支持的 Win32 控制台应用程序。

我们需要执行一些管理任务,例如设置 Tests 项目依赖于 Core 项目。我们还需要添加 Boost Test Library 头文件。模板提供了一个 main 函数,因此不需要 Visual Studio 向导提供的 _tmain。我们只需要提供 init_unit_test_suite 的实现。此函数负责初始化 MFC 并设置要运行的测试列表。新版本的 Tests.cpp 如下所示

#include "stdafx.h"

#include "boost/test/included/unit_test_framework.hpp"

#include "Tests.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// The one and only application object

CWinApp theApp;

using namespace std;
using boost::unit_test_framework::test_suite;

void AppVersionTests(test_suite* CoreTestSuite);

test_suite* init_unit_test_suite(int argc, char* argv[])
{
    // initialize MFC and print and error on failure
    if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
    {
        printf("Fatal Error: MFC initialization failed\n");
        return (0);
    }

    test_suite* CoreTestSuite = BOOST_TEST_SUITE("Core Tests");

    AppVersionTests(CoreTestSuite);

    return (CoreTestSuite);
}

void AppVersionTests(test_suite* CoreTestSuite)
{
}

我们创建了 AppVersionTests 函数来包含我们的测试,并将其添加到测试套件中。

需要对编译器选项进行一些重要的设置。“Run-Time Type Info”需要打开,并且在“Advanced”选项下的“C++ Exceptions”设置需要更改为 /EHa。现在我们应该能够构建整个解决方案了。

我们还将把运行测试添加到 Post-Build Event 中,以便每次构建解决方案时都会运行测试套件。这样,任何问题都会导致构建失败。将 "$(TargetPath)"(包括引号)添加到 Post-Build Event 命令行。此时,构建解决方案时,输出窗口应以以下内容结尾

Running Unit Tests
*** No errors detected

编写一些测试

现在来做测试。我们首先将 AppVersionTests 函数移动到一个单独的文件中。这简化了添加测试,特别是当我们为整个例程和测试库设置时。

第一个测试是简单地使用 AppVersion 对象的各种构造函数来构造它。所以我们将创建一个函数来执行这些测试,并将其添加到测试列表中。

void AppVersionConstructorTests(test_suite* CoreTestSute)
{
    AppVersion a;
    AppVersion b(1, 2, 3, 4);
    AppVersion c(b);
}
// in AppVersionTests
    CoreTestSuite->add(BOOST_TEST_CASE(&AppVersionConstrutorTests));

BOOST_TEST_CASE 行只是将函数添加到要运行的测试列表中。但是由于我们还没有创建这些构造函数,所以这甚至不会编译。所以,让我们去创建这些构造函数。

class AppVersion : public CObject
{
        AppVersion();
        AppVersion(unsigned long Major,
                   unsigned long Minor,
                   unsigned long Build,
                   unsigned long Revision);
        AppVersion(const AppVersion& b);
};

AppVersion::AppVersion() :
CObject(),
m_Major(0),
m_Minor(0),
m_Build(0),
m_Revision(0)
{
}

AppVersion::AppVersion(unsigned long Major, unsigned long Minor,
                       unsigned long Build, unsigned long Revision) :
CObject(),
m_Major(Major),
m_Minor(Minor),
m_Build(Build),
m_Revision(Revision)
{
}

AppVersion::AppVersion(const AppVersion& b) :
m_Major(b.m_Major),
m_Minor(b.m_Minor),
m_Build(b.m_Build),
m_Revision(b.m_Revision)
{
    ASSERT_VALID(&b);
}

现在我们编译这个,我们将看到

Running Unit Tests
Running 1 test case...
*** No errors detected

到目前为止,一切顺利。

添加调试支持

由于 AppVersion 是从 CObject 派生的,因此让我们利用它提供的调试支持。

CObject 包含一个 AssertValid 调试函数,允许任何人检查对象是否处于有效状态。我个人喜欢在对象的任何成员函数的第一个语句中检查对象是否有效。我也喜欢检查调用者传递给类的所有参数。这会增加一些工作量,但能够捕获错误的那几次就足以弥补了。在这种情况下,AppVersion 实际上没有无效状态,因为版本号可以是任何数字组合。所以我们将只使用 CObject 提供的 AssertValid

CObject 提供的另一个调试辅助工具是 Dump 方法。我们将重写默认的 Dump 方法,以便在请求时输出我们的状态。如果我们遇到内存泄漏或其他错误,这些信息将有助于确定与问题相关的对象。

#ifdef _DEBUG
void AppVersion::Dump(CDumpContext& dc) const
{
    dc << "AppVersion:\n";

    // call base class function first
    CObject::Dump(dc);

    dc << "Major: " << m_Major << "\n";
    dc << "Minor: " << m_Minor << "\n";
    dc << "Build: " << m_Build << "\n";
    dc << "Revision: " << m_Revision << "\n";
}
#endif

添加访问器

现在基础工作都已完成,是时候编写更多测试了。但为了检查测试是否正常工作,需要一些访问器。

class AppVersion ...
    unsigned long GetMajor() const;
    unsigned long GetMinor() const;
    unsigned long GetBuild() const;
    unsigned long GetRevision() const;
unsigned long AppVersion::GetMajor() const
{
    ASSERT_VALID(this);

    return (m_Major);
}

unsigned long AppVersion::GetMinor() const
{
    ASSERT_VALID(this);

    return (m_Minor);
}

unsigned long AppVersion::GetBuild() const
{
    ASSERT_VALID(this);

    return (m_Build);
}

unsigned long AppVersion::GetRevision() const
{
    ASSERT_VALID(this);

    return (m_Revision);
}

有几点需要注意。如上所述,我们通过在每个成员函数开头使用 ASSERT_VALID(this) 来利用 CObject 提供的调试支持。这允许我们检查对象是否处于有效状态。由于这些只在 Debug 版本中存在,因此对 Release 代码的性能没有影响。

现在我们可以检查构造函数测试和这些访问器是否工作正常。

编写更多测试

我们添加测试以真正检查我们的构造函数是否正常工作。

// in AppVersionConstructorTests
BOOST_CHECK_EQUAL(a.GetMajor(), 0);
BOOST_CHECK_EQUAL(a.GetMinor(), 0);
BOOST_CHECK_EQUAL(a.GetBuild(), 0);
BOOST_CHECK_EQUAL(a.GetRevision(), 0);

BOOST_CHECK_EQUAL(b.GetMajor(), 1);
BOOST_CHECK_EQUAL(b.GetMinor(), 2);
BOOST_CHECK_EQUAL(b.GetBuild(), 3);
BOOST_CHECK_EQUAL(b.GetRevision(), 4);

BOOST_CHECK_EQUAL(c.GetMajor(), 1);
BOOST_CHECK_EQUAL(c.GetMinor(), 2);
BOOST_CHECK_EQUAL(c.GetBuild(), 3);
BOOST_CHECK_EQUAL(c.GetRevision(), 4);

宏由 Boost Test Library 提供,并简单地检查两个值是否相等。

现在我们开始掌握编写测试的方法了。最难的部分是设置框架。一旦完成,添加测试就相当简单了。您几乎会期待编写测试并看到它们正常工作。这比尝试在大型应用程序中创建要测试的条件要快得多,也简单得多。

添加赋值运算符

让我们添加一个赋值运算符。先是测试。

void AppVersionAssignmentTests
{
    // check assignment
    AppVersion a(1, 2, 3, 4);
    AppVersion b;
    b = a;
    BOOST_CHECK_EQUAL(b.GetMajor(), 1U);
    BOOST_CHECK_EQUAL(b.GetMinor(), 2U);
    BOOST_CHECK_EQUAL(b.GetBuild(), 3U);
    BOOST_CHECK_EQUAL(b.GetRevision(), 4U);

    // check self-assignment
    b = b;
    BOOST_CHECK_EQUAL(b.GetMajor(), 1U);
    BOOST_CHECK_EQUAL(b.GetMinor(), 2U);
    BOOST_CHECK_EQUAL(b.GetBuild(), 3U);
    BOOST_CHECK_EQUAL(b.GetRevision(), 4U);
}

// in AppVersionTests
    CoreTestSuite->add(BOOST_TEST_CASE(&AppVersionAssignmentTests));

然后是代码。

// in the class definition (AppVersion.h)
AppVersion& operator=(const AppVersion& b);
// in the implementation (AppVersion.cpp)
AppVersion& AppVersion::operator=(const AppVersion& b)
{
    ASSERT_VALID(this);
    ASSERT_VALID(&b);

    m_Major = b.m_Major;
    m_Minor = b.m_Minor;
    m_Build = b.m_Build;
    m_Revision = b.m_Revision;

    return (*this);
}

operator= 的实现中有一个重要的注意事项是,我们没有检查自赋值。在这个对象中,由于我们只复制简单的整数,因此没有任何东西会失败。实现一个正确的、异常安全的 operator=,当对象有已分配的数据时,这是另一篇文章要讲的。但我们确实检查了当前对象和我们正在复制的对象是否有效。

转换为文本

我们还将提供几个函数来将版本号作为 string 返回。这些函数被实现为非成员函数,因为它们只能使用 public 接口来实现。

#include <iostream>

const CString GetText(const AppVersion& AV);

std::ostream& operator<<(std::ostream& os, const AppVersion& AV);
const CString GetText(const AppVersion& AV)
{
    ASSERT_VALID(&AV);

    CString Result;

    Result.Format("%lu.%lu.%lu.%lu",
                  AV.GetMajor(), AV.GetMinor(),
                  AV.GetBuild(), AV.GetRevision());

    return (Result);
}

ostream& operator<<(ostream& os, const AppVersion& AV)
{
    os << static_cast<LPCTSTR>(GetText(AV));

    return (os);
}
void AppVersionTextTests(void)
{
    AppVersion a(1, 2, 3, 4);

    BOOST_CHECK_EQUAL(GetText(a), "1.2.3.4");

    ostringstream os;
    os << a;
    BOOST_CHECK_EQUAL(os.str(), "1.2.3.4");
}

添加比较运算符

所以这个类很棒,但它所做的只是存储四个数字并返回它们。现在需要的是一些比较运算符,以便我们可以轻松地比较两个版本来确定哪个版本更新。我们也可能想将它们放入 STL 容器中并对其进行排序。我们的第一个想法是提供所有比较运算符的实现。

  • operator<
  • operator<=
  • operator==
  • operator!=
  • operator>=
  • operator>

这将是大量代码需要编写和正确实现。如果我们稍加思考,就会发现很多运算符可以基于其他运算符来实现。这将减少代码量并有助于保持所有操作的正确性,但我们可以做得更好!

再一次,Boost 有解决方案。有一组运算符模板,它们提供了运算符的实现,而类只需要提供很少的运算符。由于我们只关注比较运算符,因此我们只使用其中的一小部分。

要使用 Boost Operators library,我们的类需要从一些 Boost 模板派生。

#include "boost\operators.hpp"


class AppVersion : public CObject,
                   public boost::totally_ordered<AppVersion>
{
    ...

由于我们只关心比较,我们将使用 totally_ordered 模板。它只需要 operator<operator== 这两个函数。它提供了所有其他比较运算符。

先是测试。

void AppVersionComparisonTests(void)
{
    // check operators - the same
    BOOST_CHECK_EQUAL(AppVersion(0, 0, 0, 0), AppVersion(0, 0, 0, 0));
    BOOST_CHECK_EQUAL(AppVersion(0, 0, 0, 1), AppVersion(0, 0, 0, 1));
    BOOST_CHECK_EQUAL(AppVersion(0, 0, 1, 0), AppVersion(0, 0, 1, 0));
    BOOST_CHECK_EQUAL(AppVersion(0, 1, 0, 0), AppVersion(0, 1, 0, 0));
    BOOST_CHECK_EQUAL(AppVersion(1, 0, 0, 0), AppVersion(1, 0, 0, 0));

    // check operators - the same
    BOOST_CHECK_EQUAL(AppVersion(0, 0, 0, 0) <  AppVersion(0, 0, 0, 0), false);
    BOOST_CHECK_EQUAL(AppVersion(0, 0, 0, 0) <= AppVersion(0, 0, 0, 0), true);
    BOOST_CHECK_EQUAL(AppVersion(0, 0, 0, 0) == AppVersion(0, 0, 0, 0), true);
    BOOST_CHECK_EQUAL(AppVersion(0, 0, 0, 0) != AppVersion(0, 0, 0, 0), false);
    BOOST_CHECK_EQUAL(AppVersion(0, 0, 0, 0) >= AppVersion(0, 0, 0, 0), true);
    BOOST_CHECK_EQUAL(AppVersion(0, 0, 0, 0) >  AppVersion(0, 0, 0, 0), false);

    // check operators - the same
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) <  AppVersion(1, 2, 3, 4), false);
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) <= AppVersion(1, 2, 3, 4), true);
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) == AppVersion(1, 2, 3, 4), true);
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) != AppVersion(1, 2, 3, 4), false);
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) >= AppVersion(1, 2, 3, 4), true);
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) >  AppVersion(1, 2, 3, 4), false);

    // check operators - different in revision
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) <  AppVersion(1, 2, 3, 5), true);
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) <= AppVersion(1, 2, 3, 5), true);
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) == AppVersion(1, 2, 3, 5), false);
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) != AppVersion(1, 2, 3, 5), true);
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) >= AppVersion(1, 2, 3, 5), false);
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) >  AppVersion(1, 2, 3, 5), false);

    // check operators - different in build
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) <  AppVersion(1, 2, 4, 4), true);
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) <= AppVersion(1, 2, 4, 4), true);
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) == AppVersion(1, 2, 4, 4), false);
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) != AppVersion(1, 2, 4, 4), true);
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) >= AppVersion(1, 2, 4, 4), false);
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) >  AppVersion(1, 2, 4, 4), false);

    // check operators - different in minor
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) <  AppVersion(1, 3, 3, 4), true);
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) <= AppVersion(1, 3, 3, 4), true);
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) == AppVersion(1, 3, 3, 4), false);
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) != AppVersion(1, 3, 3, 4), true);
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) >= AppVersion(1, 3, 3, 4), false);
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) >  AppVersion(1, 3, 3, 4), false);

    // check operators - different in major
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) <  AppVersion(2, 2, 3, 4), true);
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) <= AppVersion(2, 2, 3, 4), true);
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) == AppVersion(2, 2, 3, 4), false);
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) != AppVersion(2, 2, 3, 4), true);
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) >= AppVersion(2, 2, 3, 4), false);
    BOOST_CHECK_EQUAL(AppVersion(1, 2, 3, 4) >  AppVersion(2, 2, 3, 4), false);
}

然后是代码。

// in the class definition
bool operator<(const AppVersion& b) const;
bool operator==(const AppVersion& b) const;
// in the implementation
bool AppVersion::operator<(const AppVersion& b) const
{
    ASSERT_VALID(this);
    ASSERT_VALID(&b);

    bool Result = false;

    if (m_Major < b.m_Major)
    {
        Result = true;
    }
    else if (m_Major == b.m_Major)
    {
        if (m_Minor < b.m_Minor)
        {
            Result = true;
        }
        else if (m_Minor == b.m_Minor)
        {
            if (m_Build < b.m_Build)
            {
                Result = true;
            }
            else if (m_Build == b.m_Build)
            {
                if (m_Revision < b.m_Revision)
                {
                    Result = true;
                }
            }
        }
    }

    return (Result);
}

bool AppVersion::operator==(const AppVersion& b) const
{
    ASSERT_VALID(this);
    ASSERT_VALID(&b);

    if ((m_Major == b.m_Major) &&
        (m_Minor == b.m_Minor) &&
        (m_Build == b.m_Build) &&
        (m_Revision == b.m_Revision))
        return (true);

    return (false);
}

哇,模板真的节省了很多工作。如果您感到好奇,想看看测试失败时会发生什么,请将第一个测试更改为比较 0.0.0.0 是否等于 0.0.0.1。

惊喜,构建输出中会出现一条错误消息,描述了错误。

Running Tests
Running 5 test case...
AppVersionTests.cpp(51): error in "AppVersionTests": test AppVersion(0, 0, 0, 0) == 
AppVersion(0, 0, 0, 1) failed [0.0.0.0 != 0.0.0.1]
*** 1 failure in test case "Core Tests"
Project : error PRJ0019: A tool returned an error code: "Running Unit Tests"

这不仅描述了错误,您还可以双击错误并直接跳转到失败的测试。

序列化支持

最后要添加的是序列化支持。由于 AppVersion 是从 CObject 派生的,因此这是一项相当直接的任务。首先是测试。

void AppVersionSerializationTests(void)
{
    // serialization test
    TCHAR TempPath[_MAX_PATH];
    if (GetTempPath(sizeof(TempPath) / sizeof(TCHAR), TempPath) == 0)
        BOOST_ERROR("Could not obtain temporary directory.");

    TCHAR TempFilename[_MAX_PATH];
    if (GetTempFileName(TempPath, "Tst", 0, TempFilename) == 0)
        BOOST_ERROR("Could not create temporary filename.");

    CFile ArchiveFile;
    if (ArchiveFile.Open(TempFilename, CFile::modeCreate | 
        CFile::modeWrite | CFile::shareExclusive | CFile::typeBinary) == 0)
        BOOST_ERROR("Could not create temporary file.");
    CArchive StoreArchive(&ArchiveFile, CArchive::store);
    AppVersion SerOut(1, 2, 3, 4);
    SerOut.Serialize(StoreArchive);
    StoreArchive.Close();
    ArchiveFile.Close();

    AppVersion SerIn;
    if (ArchiveFile.Open(TempFilename, CFile::modeRead | 
        CFile::shareExclusive | CFile::typeBinary) == 0)
        BOOST_ERROR("Could not open temporary file.");
    CArchive LoadArchive(&ArchiveFile, CArchive::load);
    SerIn.Serialize(LoadArchive);
    LoadArchive.Close();
    ArchiveFile.Close();

    BOOST_CHECK_EQUAL(SerOut, SerIn);

    CFile::Remove(TempFilename);
}

然后是代码。

IMPLEMENT_SERIAL(AppVersion, CObject, VERSIONABLE_SCHEMA | 1)

void AppVersion::Serialize(CArchive& ar)
{
    ASSERT_VALID(this);

    CObject::Serialize(ar);
    ar.SerializeClass(GetRuntimeClass());

    if (ar.IsStoring())
    {
        // store the data
        ar << m_Major;
        ar << m_Minor;
        ar << m_Build;
        ar << m_Revision;
    }
    else
    {
        // load the data
        unsigned int Schema;
        Schema = ar.GetObjectSchema();
        switch (Schema)
        {
            case 1 :
                ar >> m_Major;
                ar >> m_Minor;
                ar >> m_Build;
                ar >> m_Revision;
                break;

            default :
                AfxThrowArchiveException(CArchiveException::badSchema, "AppVersion");
                break;
        }
    }
}

我们完成了

本文档在相对较短的篇幅内涵盖了许多主题。我们只触及了我们使用的两个 Boost 库的表面。这两个库以及 Boost 集合中的其他库都提供了更多功能。

我们没有涵盖的一些主题是异常测试和 Unicode。我们的对象可能引起异常的唯一地方是在序列化期间。目前,Boost Test library 只支持 std::string,不支持 std::wstring。处理所有转换问题会使本文档过于复杂。如果有兴趣,可以在以后的文章中涵盖这些内容。Boost 模板中还会出现一些警告。Boost 库在不断改进,因此我预计这些问题将在未来的 Boost 版本中得到解决,并且编译器会提供更好的模板支持。

由于该对象不使用动态分配的内存,因此可以使用编译器生成的默认构造函数、复制构造函数和赋值运算符。我包含了它们是为了清楚地说明测试和代码是如何协同工作的。

开发单元测试时的一个有用技术是使用代码覆盖率工具。这是一个验证所有代码路径都有测试的好方法。

我希望本文能激励您为自己编写的代码添加单元测试。一旦测试结构到位,随着时间的推移添加测试就很快了。通过将测试作为构建的一部分运行,您永远不会忘记测试代码。

我希望您也能受到启发,去了解 Boost 库的其他功能。

历史

  • 2004年8月15日:原始文章

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.