2016-08-14 2 views
-4

저는 C++을 처음 접했고 약간의 어려움을 겪고 있습니다. C- 문자열을 감싸는 String 클래스를 만들려고합니다.C++에서 메모리 누수가 발견되지 않음

얼마 후 나는 힙에 메모리 누수가 있다는 것을 알았지 만 실수를 발견하지 못했습니다.

#include <iostream> 
#include <memory> 
using namespace std; 

class String final { 
    size_t m_len; 
    size_t m_start; 
    shared_ptr<char> m_string; 

public: 
    String();     // Standard Constr 
    String(const String& s); // Copy Constr 
    String(const char* s);  // Typconvert Constr 

           // Deconstr 
    ~String(); 
} 

이는 통화 당에서 생성자은 다음과 같습니다 :

내 헤더입니다

#include "stdafx.h" 
#include "MyString.h" 
#include <iostream> 

using namespace std; 

String::String() : m_len(0), m_start(0), m_string(nullptr) 
{ 
} 

String::String(const String &obj) : m_len(obj.m_len), m_start(obj.m_start), m_string(obj.m_string) 
{ 
} 

String::String(const char* obj) : m_len(strlen(obj)) 
{ 
    shared_ptr<char> ptr(unique_ptr<char[]>(new char[m_len])); 
    m_string = ptr; 

    for (size_t i = 0; i <= m_len; i++) { 
     *(m_string.get() + i*sizeof(char)) = obj[i]; 
     if (i == m_len) 
     { 
      *(m_string.get() + i*sizeof(char)) = '\0'; 
     } 
    } 
} 

String::~String() 
{ 
    m_string.reset(); 
} 

가 그리고 이것은 내 주 :

#include "stdafx.h" 
#include "MyString.h" 
#include <iostream> 

#define _CRTDBG_MAP_ALLOC 
#include <stdlib.h> 
#include <crtdbg.h> 

using namespace std; 

int main() 
{ 
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); 
    String s0; 
    String s1(""); 
    String s2("abc"); 
    String s3("ab"); 

    String s00(s0); 
    String s11(s1); 
    String s22(s2); 

    _CrtDumpMemoryLeaks(); 
    return 0; 
} 

지금 나는이 프로그램을 실행할 때 오류가 발생합니다.

Detected memory leaks! 
Dumping objects -> 
{66} normal block at 0x00668458, 16 bytes long. 
Data: <X    f > 58 AB 0E 01 01 00 00 00 01 00 00 00 D8 8D 66 00 
{65} normal block at 0x00668DD8, 2 bytes long. 
Data: <ab> 61 62 
{64} normal block at 0x00668D98, 16 bytes long. 
Data: <X    f > 58 AB 0E 01 02 00 00 00 01 00 00 00 F8 91 66 00 
{63} normal block at 0x006691F8, 3 bytes long. 
Data: <abc> 61 62 63 
{62} normal block at 0x00668C60, 16 bytes long. 
Data: <X    f > 58 AB 0E 01 02 00 00 00 01 00 00 00 D8 8F 66 00 
{61} normal block at 0x00668FD8, 0 bytes long. 
Data: <> öß³‘@þÿÿÿ÷O 
Object dump complete. 
Debug Error! 

HEAP CORRUPTION DETECTED: after Normal block (#65) at 0x00668DD8. 
CRT detected that the application wrote to memory after end of heap buffer. 

어디에서 오는 것인지 알 수 없습니다. 나는 deconstructor에 대해 생각하고 꺼내려고 노력했기 때문에 나를 위해 하나를 쓸 것이지만 그것은 작동하지 않았다. 또한 프로그램을 중단시키는 코드 블록은 다음과 같습니다.

void* __CRTDECL operator new(size_t const size) 
{ 
    for (;;) 
    { 
    if (void* const block = malloc(size)) 
    { 
     return block; 
    } 

    if (_callnewh(size) == 0) 
    { 
     if (size == SIZE_MAX) 
     { 
      __scrt_throw_std_bad_array_new_length(); 
     } 
     else 
     { 
      __scrt_throw_std_bad_alloc(); 
     } 
    } 

    // The new handler was successful; try to allocate again... 
} 
} 

from new_scalar.cpp.

도움을 주셔서 감사합니다.

+3

이 모든 것을 관리하는'std :: string'이 있다는 것을 알고 계셨습니까? – JVApen

+1

전체 규칙 3 개 (일부 규칙 및 5 규칙, 규칙 0 등)에 대해 일부 회원이 누락되었습니다. 당신은 또한 올바른 범위에 버려지지 않습니다. 메모리는 디버그 덤프가 발생한 후에 만 ​​회수됩니다. http://stackoverflow.com/a/38437543/3747990. s0 이전과 s22 후에 중괄호를 추가하십시오. – Niall

+0

너무 단순한 작업을 위해 너무 복잡한 코드를 본 적이 없습니다! 도대체 클래스에서 포인터를 정의하고 메소드에서 포인터를 다시 정의하고 다른 포인터에 하나를 지정합니다 ... strlen을 사용하지만 strcpy는 사용하지 않습니까? 왜? 바퀴를 재발 명하는 것은 좋은 직업 인 것처럼 보인다. 고유 포인터 주위에 공유 포인터 ... 문자열 메모리의 소유자는 누구입니까? 그 물체? – Klaus

답변

1

사용 Valgrind의 당신은 당신의 문제 참조 : 새로운 [나 strlen] 고정 후

==20490== Memcheck, a memory error detector 
==20490== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al. 
==20490== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info 
==20490== Command: go 
==20490== 
==20490== Invalid write of size 1 
==20490== at 0x8048C76: String::String(char const*) (main.cpp:38) 
==20490== by 0x80487E6: main (main.cpp:59) 
==20490== Address 0x443fa58 is 0 bytes after a block of size 0 alloc'd 
==20490== at 0x402AF41: operator new[](unsigned int) (vg_replace_malloc.c:417) 
==20490== by 0x8048BDC: String::String(char const*) (main.cpp:34) 
==20490== by 0x80487E6: main (main.cpp:59) 
==20490== 
==20490== Invalid write of size 1 
==20490== at 0x8048C82: String::String(char const*) (main.cpp:41) 
==20490== by 0x80487E6: main (main.cpp:59) 
==20490== Address 0x443fa58 is 0 bytes after a block of size 0 alloc'd 
==20490== at 0x402AF41: operator new[](unsigned int) (vg_replace_malloc.c:417) 
==20490== by 0x8048BDC: String::String(char const*) (main.cpp:34) 
==20490== by 0x80487E6: main (main.cpp:59) 
==20490== 
==20490== Invalid write of size 1 
==20490== at 0x8048C76: String::String(char const*) (main.cpp:38) 
==20490== by 0x80487F6: main (main.cpp:60) 
==20490== Address 0x443facb is 0 bytes after a block of size 3 alloc'd 
==20490== at 0x402AF41: operator new[](unsigned int) (vg_replace_malloc.c:417) 
==20490== by 0x8048BDC: String::String(char const*) (main.cpp:34) 
==20490== by 0x80487F6: main (main.cpp:60) 
==20490== 
==20490== Invalid write of size 1 
==20490== at 0x8048C82: String::String(char const*) (main.cpp:41) 
==20490== by 0x80487F6: main (main.cpp:60) 
==20490== Address 0x443facb is 0 bytes after a block of size 3 alloc'd 
==20490== at 0x402AF41: operator new[](unsigned int) (vg_replace_malloc.c:417) 
==20490== by 0x8048BDC: String::String(char const*) (main.cpp:34) 
==20490== by 0x80487F6: main (main.cpp:60) 
==20490== 
==20490== Invalid write of size 1 
==20490== at 0x8048C76: String::String(char const*) (main.cpp:38) 
==20490== by 0x8048806: main (main.cpp:61) 
==20490== Address 0x443fb42 is 0 bytes after a block of size 2 alloc'd 
==20490== at 0x402AF41: operator new[](unsigned int) (vg_replace_malloc.c:417) 
==20490== by 0x8048BDC: String::String(char const*) (main.cpp:34) 
==20490== by 0x8048806: main (main.cpp:61) 
==20490== 
==20490== Invalid write of size 1 
==20490== at 0x8048C82: String::String(char const*) (main.cpp:41) 
==20490== by 0x8048806: main (main.cpp:61) 
==20490== Address 0x443fb42 is 0 bytes after a block of size 2 alloc'd 
==20490== at 0x402AF41: operator new[](unsigned int) (vg_replace_malloc.c:417) 
==20490== by 0x8048BDC: String::String(char const*) (main.cpp:34) 
==20490== by 0x8048806: main (main.cpp:61) 
==20490== 
==20490== 
==20490== HEAP SUMMARY: 
==20490==  in use at exit: 18,944 bytes in 1 blocks 
==20490== total heap usage: 7 allocs, 6 frees, 18,997 bytes allocated 
==20490== 
==20490== LEAK SUMMARY: 
==20490== definitely lost: 0 bytes in 0 blocks 
==20490== indirectly lost: 0 bytes in 0 blocks 
==20490==  possibly lost: 0 bytes in 0 blocks 
==20490== still reachable: 18,944 bytes in 1 blocks 
==20490==   suppressed: 0 bytes in 0 blocks 
==20490== Rerun with --leak-check=full to see details of leaked memory 
==20490== 
==20490== For counts of detected and suppressed errors, rerun with: -v 
==20490== ERROR SUMMARY: 6 errors from 6 contexts (suppressed: 0 from 0) 

새로운 [나 strlen + 1] ->

==21539== Memcheck, a memory error detector 
==21539== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al. 
==21539== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info 
==21539== Command: go 
==21539== 
==21539== 
==21539== HEAP SUMMARY: 
==21539==  in use at exit: 18,944 bytes in 1 blocks 
==21539== total heap usage: 7 allocs, 6 frees, 19,000 bytes allocated 
==21539== 
==21539== LEAK SUMMARY: 
==21539== definitely lost: 0 bytes in 0 blocks 
==21539== indirectly lost: 0 bytes in 0 blocks 
==21539==  possibly lost: 0 bytes in 0 blocks 
==21539== still reachable: 18,944 bytes in 1 blocks 
==21539==   suppressed: 0 bytes in 0 blocks 
==21539== Rerun with --leak-check=full to see details of leaked memory 
==21539== 
==21539== For counts of detected and suppressed errors, rerun with: -v 
==21539== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) 

그러나 코드에 대한 몇 가지 발언 :

String::~String() 
{ 
    m_string.reset(); 
} 

왜? 클래스가 파괴되면 포인터가 파괴되어 자동으로 포인터 자체를 해제합니다. 그것을 두 번하는 것은 어리 석다! 소멸자를 비워두고, 자동 소멸자를 사용하십시오!

for (size_t i = 0; i <= m_len; i++) { 
    *(m_string.get() + i*sizeof(char)) = obj[i]; 
    if (i == m_len) 
    { 
     *(m_string.get() + i*sizeof(char)) = '\0'; 
    } 
} 

strcpy과 같습니다. strlen을 사용하여 크기를 얻는다면 libs의 복사본을 사용할 수도 있습니다.

String::String(const char* obj) : m_len(strlen(obj)), m_string{ new char[m_len+1], delArray<char>() } { ... } 

delArray이 어디 :

String::String(const char* obj) : m_len(strlen(obj)) 
{ 
    shared_ptr<char> ptr(unique_ptr<char[]>(new char[m_len])); 
    m_string = ptr; 
는 교체해야합니다

template< typename T > 
struct delArray { void operator()(T const * p) { delete[] p; } }; 

내가 찾은

+0

@ M.M : 죄송합니다. 삭제했습니다. 감사합니다. – Klaus

+0

답변 해 주셔서 감사합니다. 그것은 지금도 일했습니다. 심지어 6502가 이미 지적했듯이 시도했습니다. 또한 코드에 대한 피드백을 주셔서 감사합니다. 많은 감사를드립니다. 왜 unique_ptr을 꺼내고 바꿔야합니까? – Mauravan

+0

죄송합니다. 그러나 공유하고 고유 한 포인터 조합이 무엇에 좋은지 이해하지 못했습니다. 코드가 공유되지 않아야하는 단일 인스턴스를 소유하는 경우 고유 한 포인터를 사용하십시오. 리소스를 공유하는 경우 공유 된 리소스를 사용하십시오. 나는 당신이 당신의 객체의 사용자에 대한 포인터에 대한 외부 액세스를 제공하는지 전혀 모른다. 그래서 그것은 다릅니다 :-)하지만 std :: string을 사용하고 코드를 잊어 버려야합니다. 내가 믿는 것을 배우기에 좋은 것입니다. 문자열 클래스는 또한'move' 등을 알고 있어야합니다. 따라서 실제 응용 프로그램에서 코드를 사용하지 마십시오! – Klaus

1

코드는

new char[m_len] 

정확히 m_len 문자를 할당하지만 당신은 때문에 m_len+1 문자를 사용하고있는 NUL \0을 (당신이 실제로 한 번 한 번 <=와 함께 루프를 들어, 배를 사용할 수없는 위치로 쓰고있어 종료 어떤 이상한 이유 때문에 다시 명백하게).

+0

답장을 보내 주셔서 감사합니다. 코드를'shared_ptr ptr (unique_ptr (new char [m_len + 1]));로 변경했습니다. \t m_string = ptr; 위한 \t (I 0 = size_t가, 난 <= m_len; 내가 ++) { \t \t \t \t 경우 (! I = m_len) \t \t { \t \t \t * (m_string.get() + 난를 sizeof * (char)) = obj [i]; 다른 \t \t \t \t \t } { \t \t \t \t \t * (m_string.get() + 난를 sizeof (* CHAR)) = '\ 0'; \t \t} \t} 그러나 오류가 지속되는 것 같습니다. – Mauravan

-1

class String

좀 더 특별한 코드 조각은 ...있다 당신의 잘못.

표준 문자열 클래스에 대한 문자열 클래스를 바꾼 후 문제를 재현 할 수 없습니다.