防御性编程

防御性编程

我们常用断言来达到这个目的。工作完成后,我们用NDEBUG来去掉断言。如下所示:

#ifdef NDEBUG

  #define assert(cond) ((void)0)

#else

  void assertImpl(const char*, const char*, long);

#define assert(cond) \

  ((cond) ? (void)0 : assertImpl(???))

#endif

Thinking in C++前几章笔记(一)

http://blog.163.com/zhoumhan_0351/blog/static/39954227201032124942513

    如想开启或关闭程序中某些位置的断言,不但必须#define或#undef NDEBUG,而且还要重新包含<cassert>。

前置条件是使用该函数的必要条件,后置条件是该函数提供的结果,通过返回值或通过(by) side-effect.

The preconditions in derived classes must impose no extra requirements 

beyond those in the base contract, and the postconditions must deliver at least 

as much.

1、测试程序+编码比直接编码更快。最好用布尔表达式的集合来表示测试问题。

如下是一个例子

#ifndef DATE1_H

#define DATE1_H

#include <string>

class Date {

public:

  // A struct to hold elapsed time:

  Date();

  Date(int year, int month, int day):years(year),months(month),days(day){}

  int getYear() const {

  return years;

  }

  int getMonth() const{

  return months;

  }

  int getDay() const{

  return days;

  }

private:

 int years;

    int months;

    int days;

};

#endif // DATE1_H ///:~

//: C02:SimpleDateTest.cpp

#include <iostream>

using namespace std;

// Test machinery

int nPass = 0, nFail = 0;

void test(bool t) { if(t) nPass++; else nFail++; }

int main() {

  Date mybday(1951, 10, 1);

  test(mybday.getYear() == 1951);

  test(mybday.getMonth() == 10);

  test(mybday.getDay() == 1);

  cout << "Passed: " << nPass << ", Failed: "

       << nFail << endl;

}

2、TestSuite框架

如cppunit(http://sourceforge.net/apps/mediawiki/cppunit/index.php?title=Main_Page)

类名为 TestSuite。两个主要的类,test,suite.test是一个抽象基类,可以从这个类中派生用户自己的测试对象,只要重写run就行了。

http://junit.sourceforge.net/junit3.8.1/javadoc/junit/framework/TestSuite.html

讲述如何使用这个套件。

//: C02:Date1.h

// A first pass at Date.h.

#ifndef DATETEST_H

#define DATETEST_H

#include "../TestSuite/Test.h"

class DateTest : public TestSuite::Test {

  Date mybday;

  Date today;

  Date myevebday;

public:

  DateTest(): mybday(1951, 10, 1), myevebday("19510930") {}

  void run() {

    testOps();

    testFunctions();

    testDuration();

  }

  void testOps() {

    test_(mybday < today);

    ...

  }

  void testFunctions() {

    test_(mybday.getYear() == 1951);

    ...

  }

  void testDuration() {

    Date d2(2003, 7, 4);

    Date::Duration dur = duration(mybday, d2);

    test_(dur.years == 51);

    ...

  }

};

#endif // DATETEST_H ///:~

Running the test is a simple matter of instantiating a DateTest object and calling its run( ) member 

function:

//: C02:DateTest.cpp

// Automated testing (with a framework).

//{L} Date ../TestSuite/Test

#include <iostream>

#include "DateTest.h"

using namespace std;

int main() {

  DateTest test;

  test.run();

  return test.report();

}

Test类使用运行时类型识别(RTTI)来取得测试类的类名。默认将结果输出到屏幕上。可以用成员函数setStream()将结果输出到文件中。此外,还要fail_(),succeed_()函数等。

3、测试技术

(1)跟踪宏

定义如下类似的跟踪宏来进行:

#define TRACE(ARG) cout << #ARG << endl; ARG

#define D(a) cout << #a "=[" << a << "]" << endl;

(2)跟踪文件

定义如下格式

#include <iostream>

#include <fstream>

#ifndef TRACE_H

#define TRACE_H

#include <fstream>

#define TRACEON

#ifdef TRACEON

std::ofstream TRACEFILE__("C:\\TRACE.OUT");

#define cout TRACEFILE__

#endif //ifdef

#endif // TRACE_H ///:~

using namespace std;

int main() {

 ifstream f("C:\\bsmain_runtime.log");

    cout << f.rdbuf(); // Dumps file contents to file

} ///:~

    如上所示,我们重定义了cout,通过定义一个文件,而进行跟踪。注意要在iostream后定义,不然会被其覆盖,达不到想要的效果了。当然这样定义是不符合C++标准的。

4、发现内存泄露

我们通过对new和delete分配次数进行计数,看其是否匹配,从而达到这个目的。重写new和delete。并且运用了宏FILE_和LINE_来存储文件名和行号。

//: C03:Tracetst.cpp {-bor}

#include <iostream>

#include <vector>

#include <cstring>

#ifndef MEMCHECK_H

#define MEMCHECK_H

#include <cstddef>  // For size_t

// Usurp the new operator (both scalar and array versions)

void* operator new(std::size_t, const char*, long);

void* operator new[](std::size_t, const char*, long);

#define new new (__FILE__, __LINE__)

extern bool traceFlag;

#define TRACE_ON() traceFlag = true

#define TRACE_OFF() traceFlag = false

extern bool activeFlag;

#define MEM_ON() activeFlag = true

#define MEM_OFF() activeFlag = false

#endif // MEMCHECK_H ///:~

//: C02:MemCheck.cpp {O}

#include <cstdio>

#include <cstdlib>

#include <cassert>

#include <cstddef>

using namespace std;

#undef new

 

// Global flags set by macros in MemCheck.h

bool traceFlag = true;

bool activeFlag = false;

namespace {

// Memory map entry type

struct Info {

  void* ptr;

  const char* file;

  long line;

};

// Memory map data

const size_t MAXPTRS = 10000u;

Info memMap[MAXPTRS];

size_t nptrs = 0;

// Searches the map for an address

int findPtr(void* p) {

  for(size_t i = 0; i < nptrs; ++i)

    if(memMap[i].ptr == p)

      return i;

  return -1;

void delPtr(void* p) {

  int pos = findPtr(p);

  // Remove pointer from map

  for(size_t i = pos; i < nptrs-1; ++i)

    memMap[i] = memMap[i+1];

  --nptrs;

}

// Dummy type for static destructor

struct Sentinel {

  ~Sentinel() {

    if(nptrs > 0) {

      printf("Leaked memory at:\n");

      for(size_t i = 0; i < nptrs; ++i)

        printf("\t%p (file: %s, line %ld)\n",

          memMap[i].ptr, memMap[i].file, memMap[i].line);

    }

    else

      printf("No user memory leaks!\n");

  }

};

// Static dummy object

Sentinel s;

} // End anonymous namespace

// Overload scalar new

void*

operator new(size_t siz, const char* file, long line) {

  void* p = malloc(siz);

  if(activeFlag) {

    if(nptrs == MAXPTRS) {

      printf("memory map too small (increase MAXPTRS)\n");

      exit(1);

    }

    memMap[nptrs].ptr = p;

    memMap[nptrs].file = file;

    memMap[nptrs].line = line;

    ++nptrs;

  }

  if(traceFlag) {

    printf("Allocated %u bytes at address %p ", siz, p);

    printf("(file: %s, line: %ld)\n", file, line);

  }

  return p;

}

// Overload array new

void*

operator new[](size_t siz, const char* file, long line) {

  return operator new(siz, file, line);

}

// Override scalar delete

void operator delete(void* p) {

  if(findPtr(p) >= 0) {

    free(p);

    delPtr(p);

    if(traceFlag)

      printf("Deleted memory at address %p\n", p);

  }

  else if(!p && activeFlag)

    printf("Attempt to delete unknown pointer: %p\n", p);

}

// Override array delete

void operator delete[](void* p) {

  operator delete(p);

} ///:~

//: C02:MemTest.cpp

//{L} MemCheck

// Test of MemCheck system.

using namespace std;

class Foo {

  char* s;

public:

  Foo(const char*s ) {

    this->s = new char[strlen(s) + 1];

    strcpy(this->s, s);

  }

  ~Foo() { delete [] s; }

};

int main() {

  MEM_ON();

  cout << "hello" << endl;

  int* p = new int;

  delete p;

  int* q = new int[3];

  delete [] q;

  int* r;

  delete r;

  vector<int> v;

  v.push_back(1);

  Foo s("goodbye");

  MEM_OFF();

} ///:~

说明:程序在不同的编译器运行结果不一定相同,只是体现一种思想,即我们可以通过重载new和delete,new[],delete[],增加一些辅助的数据来,统计分配和释放是否一致。

断言只能用于不变量条件的检查,而不能将其用于运行时错误处理。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值