c ++内存泄漏 - 提升库

时间:2016-05-22 11:49:10

标签: c++ c++11 boost memory-leaks

我在以下程序中观察到内存泄漏:

// g++ -std=c++11 32_MyTime.cpp 
//
// valgrind --leak-check=full ./a.out 
//


#include <boost/date_time/posix_time/posix_time.hpp>
#include <iostream>

using namespace std;

class MyTime {
private:
  int year;
  int month;
  int day;
  int hour;
  boost::posix_time::ptime *ptrTime;
  void update() {
    year = ptrTime->date().year();
    month = ptrTime->date().month();
    day = ptrTime->date().day();
    hour = ptrTime->time_of_day().hours(); 
  }
public:
  ~MyTime() {
    if (ptrTime!=nullptr) delete ptrTime;
  }
  MyTime(int y, int m, int d, int h):year{y},month{m},day{d},hour{h} {
    ptrTime = new boost::posix_time::ptime(
    boost::gregorian::date(year, month, day),
    boost::posix_time::hours(hour));       
  }
  void addHours(int nHours) {
    if (ptrTime!=nullptr) delete ptrTime;
    ptrTime = new boost::posix_time::ptime(
        boost::gregorian::date(year, month, day),
        boost::posix_time::hours(hour+nHours));
    update();
  }
  int getYear()  const { return year; }
  int getMonth() const { return month; }
  int getDay()   const { return day; }
  int getHour()  const { return hour; }
  void set(int y, int m, int d, int h) {
    if (ptrTime!=nullptr) delete ptrTime;
    ptrTime = new boost::posix_time::ptime(
        boost::gregorian::date(y, m, d),
        boost::posix_time::hours(h));
    update();
  }
  time_t getSecSince() const {
    return (*ptrTime - boost::posix_time::ptime(boost::gregorian::date(1970, 1, 1))).total_seconds();
  }
  const boost::posix_time::ptime* getPTime() const {
    return ptrTime;
  }
  bool operator==(const MyTime& other) const {
    return *ptrTime==*other.getPTime();
  }
  bool operator!=(const MyTime& other) const {
    return !(*this == other);
  }
  bool operator<(const MyTime& other) const {
    return *ptrTime < *(other.getPTime());
  }
  bool operator<=(const MyTime& other) const {
    return *ptrTime <= *(other.getPTime());
  }
  bool operator>(const MyTime& other) const {
    return *ptrTime > *other.getPTime();
  }
  bool operator>=(const MyTime& other) const {
    return *ptrTime >= *other.getPTime();
  }
};

void test() {

    MyTime t1 {2016,5,21,17};
    MyTime t2 {2016,5,22,10};

    bool print = false;

    if (print) {
    cout << "From : " << *t1.getPTime() << endl;  // Memory leak
    cout << "To   : " << *t2.getPTime() << endl;  // Memory leak
    }

    cout << " t1 < t2  " << (t1<t2)  << endl;
        cout << " t1 > t2  " << (t1>t2)  << endl;
        cout << " t1 == t2 " << (t1==t2) << endl;
        cout << " t1 < t1  " << (t1<t1)  << endl;
        cout << " t1 > t1  " << (t1>t1)  << endl;
        cout << " t1 == t1 " << (t1==t1) << endl;


    while (t1<=t2) {
      if (print)
            cout << "> Time : " << *t1.getPTime() << "    " << t1.getSecSince() << endl;  // Memory leak
      int s = t1.getSecSince();
      t1.addHours(1);
    }


}

int main() {
  test();
}

我用valgrind分析了程序,并且该类应该没有错误(从内存泄漏的角度来看)。

该指令导致泄漏:

cout << "From : " << *t1.getPTime() << endl;  // Memory leak

但我不明白为什么。 我假设boost库没有错误。 它可能是一些复制构造函数(只是一个解释尝试)。

我怎么能避免它?

对我来说解决问题并不重要(代码仅用于调试),但重要的是要理解为什么它会在将来避免类似的错误。

编辑代码实际上没有内存泄漏。您必须打开打印才能获得泄漏:print = true;

编辑2 使用print = true;

执行Valgrind
==22204== 
==22204== HEAP SUMMARY:
==22204==     in use at exit: 1,748 bytes in 31 blocks
==22204==   total heap usage: 223 allocs, 192 frees, 38,751 bytes allocated
==22204== 
==22204== LEAK SUMMARY:
==22204==    definitely lost: 0 bytes in 0 blocks
==22204==    indirectly lost: 0 bytes in 0 blocks
==22204==      possibly lost: 619 bytes in 20 blocks
==22204==    still reachable: 1,129 bytes in 11 blocks
==22204==         suppressed: 0 bytes in 0 blocks
==22204== Rerun with --leak-check=full to see details of leaked memory
==22204== 
==22204== For counts of detected and suppressed errors, rerun with: -v
==22204== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)

使用print = true;和更多cout执行Valgrind:

==22277== HEAP SUMMARY:
==22277==     in use at exit: 1,748 bytes in 31 blocks
==22277==   total heap usage: 79,279 allocs, 79,248 frees, 15,972,927 bytes allocated
==22277== 
==22277== LEAK SUMMARY:
==22277==    definitely lost: 0 bytes in 0 blocks
==22277==    indirectly lost: 0 bytes in 0 blocks
==22277==      possibly lost: 619 bytes in 20 blocks
==22277==    still reachable: 1,129 bytes in 11 blocks
==22277==         suppressed: 0 bytes in 0 blocks
==22277== Rerun with --leak-check=full to see details of leaked memory
==22277== 
==22277== For counts of detected and suppressed errors, rerun with: -v
==22277== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)

使用空main执行Valgrind:

==22211== Memcheck, a memory error detector
==22211== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==22211== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info
==22211== Command: ./a.out
==22211== 
==22211== 
==22211== HEAP SUMMARY:
==22211==     in use at exit: 0 bytes in 0 blocks
==22211==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==22211== 
==22211== All heap blocks were freed -- no leaks are possible
==22211== 
==22211== For counts of detected and suppressed errors, rerun with: -v
==22211== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)

1 个答案:

答案 0 :(得分:1)

确定。泄漏是一个错误的报告:

==20323== 72,704 bytes in 1 blocks are still reachable in loss record 1 of 1
==20323==    at 0x4C2BBCF: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20323==    by 0x4EC21FF: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==20323==    by 0x4010609: call_init.part.0 (dl-init.c:72)
==20323==    by 0x401071A: call_init (dl-init.c:30)
==20323==    by 0x401071A: _dl_init (dl-init.c:120)
==20323==    by 0x4000D09: ??? (in /lib/x86_64-linux-gnu/ld-2.21.so)
==20323== 

正如您所看到的那样,它是从某个共享库初始化程序中分配的。如果从main删除所有代码,它将保持不变。

对于print = true我注意到泄露的方面(按照设计)。

其次,正如其他人所说,不要做new事。没有必要模仿Java的{strike> badness 和invite all kinds of programmer error

只需使用ptime会员即可。说实话,我甚至不知道你为什么要复制yearmonthdayhour字段,但是你在这里:

<强> Live On Coliru

我建议首先删除所有重复:

<强> Live On Coliru

#include <boost/date_time/posix_time/posix_time.hpp>
#include <iostream>

class MyTime : public boost::posix_time::ptime {

    using ptime = boost::posix_time::ptime;
    using date  = boost::gregorian::date;

  public:
    MyTime(short unsigned y, short unsigned m, short unsigned d, short unsigned h) 
        : ptime {date{y, m, d}, boost::posix_time::hours(h)}
    { }

    void addHours(int nHours) { *this += boost::posix_time::hours(nHours); }

    int getYear()  const { return date().year();  }
    int getMonth() const { return date().month(); }
    int getDay()   const { return date().day();   }
    int getHour()  const { return time_of_day().hours(); }

    // instead of set, just assign `v = { 2016, 5, 22, 9 }`
    time_t getSecSince() const { return (*this - ptime{date{1970, 1, 1}}).total_seconds(); }
};

void test() {

    MyTime t1{ 2016, 5, 21, 17 };
    MyTime t2{ 2016, 5, 22, 10 };

    bool print = false;

    if (print) {
        std::cout << "From : " << t1 << std::endl;
        std::cout << "To   : " << t2 << std::endl;
    }

    std::cout << " t1 < t2  " << (t1 < t2)  << std::endl;
    std::cout << " t1 > t2  " << (t1 > t2)  << std::endl;
    std::cout << " t1 == t2 " << (t1 == t2) << std::endl;
    std::cout << " t1 < t1  " << (t1 < t1)  << std::endl;
    std::cout << " t1 > t1  " << (t1 > t1)  << std::endl;
    std::cout << " t1 == t1 " << (t1 == t1) << std::endl;

    while (t1 <= t2) {
        if (print)
            std::cout << "> Time : " << t1 << "    " << t1.getSecSince() << std::endl;
        int s = t1.getSecSince();
        t1.addHours(1);
    }
}

int main() {
    test(); 
}

BONUS替代设计

由于MyTime现在只是ptime上的额外操作,为什么不呢:

<强> Live On Coliru

using MyTime = boost::posix_time::ptime;
using MyDate = boost::gregorian::date;

MyTime make_hdate(short unsigned y, short unsigned m, short unsigned d, short unsigned h) {
    return { MyDate{ y, m, d }, boost::posix_time::hours(h) };
}

MyTime addHours(MyTime const &mt, int nHours) {
    return mt + boost::posix_time::hours(nHours); 
}

time_t getSecSince(MyTime const &mt) {
    return (mt - MyTime{ MyDate{ 1970, 1, 1 } }).total_seconds(); 
}