C++구문 클래스

8223 단어 C++문고리
이전 파일 은 C++프 록 시 클래스 의 사용 장면 과 실현 방법 을 소 개 했 지만 프 록 시 클래스 에 어느 정도 결함 이 존재 합 니 다.즉,모든 프 록 시 클래스 가 새로운 대상 을 만 들 고 불필요 한 메모리 복사 를 피 할 수 없습니다.이 글 은 문형 류 를 도입 하여 프 록 시 클래스 의 다 형 성 을 유지 하 는 동시에 불필요 한 대상 복 제 를 피 할 수 있 습 니 다.
우 리 는 먼저 간단 하고 쉬 운 문자열 패 키 징 류 를 살 펴 보 겠 습 니 다.MyString 은 코드 를 쉽게 보기 위해 함수 의 성명 과 실현 을 함께 놓 았 습 니 다.

class MyString
{
public:
 //       
 MyString()
 {
  std::cout << "MyString()" << std::endl;

  buf_ = new char[1];
  buf_[0] = '\0';
  len_ = 0;
 }

 // const char*       
 MyString(const char* str)
 {
  std::cout << "MyString(const char* str)" << std::endl;

  if (str == nullptr)
  {
   len_ = 0;
   buf_ = new char[1];
   buf_[0] = '\0';
  }
  else
  {
   len_ = strlen(str);
   buf_ = new char[len_ + 1];
   strcpy_s(buf_, len_ + 1, str);
  }
 }

 //       
 MyString(const MyString& other)
 {
  std::cout << "MyString(const MyString& other)" << std::endl;

  len_ = strlen(other.buf_);
  buf_ = new char[len_ + 1];
  strcpy_s(buf_, len_ + 1, other.buf_);
 }

 // str1 = str2;
 const MyString& operator=(const MyString& other)
 {
  std::cout << "MyString::operator=(const MyString& other)" << std::endl;

  //          
  if (this != &other)
  {
   if (other.len_ > this->len_)
   {
    delete[]buf_;
    buf_ = new char[other.len_ + 1];
   }

   len_ = other.len_;
   strcpy_s(buf_, len_ + 1, other.buf_);
  }

  return *this;
 }

 // str = "hello!";
 const MyString& operator=(const char* str)
 {
  assert(str != nullptr);

  std::cout << "operator=(const char* str)" << std::endl;

  size_t strLen = strlen(str);
  if (strLen > len_)
  {
   delete[]buf_;
   buf_ = new char[strLen + 1];
  }

  len_ = strLen;
  strcpy_s(buf_, len_ + 1, str);
  
  return *this;
 }
 
 // str += "hello"
 void operator+=(const char* str)
 {
  assert(str != nullptr);

  std::cout << "operator+=(const char* str)" << std::endl;

  if (strlen(str) == 0)
  {
   return;
  }

  size_t newBufLen = strlen(str) + len_ + 1;
  char* newBuf = new char[newBufLen];
  strcpy_s(newBuf, newBufLen, buf_);
  strcat_s(newBuf, newBufLen, str);

  delete[]buf_;
  buf_ = newBuf;

  len_ = strlen(buf_);
 }

 //    ostream  <<    ,   std::cout << MyString    
 friend std::ostream& operator<<(std::ostream &out, MyString& obj)
 {
  out << obj.c_str();
  return out;
 }

 //    C      
 const char* c_str()
 {
  return buf_;
 }

 //        
 size_t length()
 {
  return len_;
 }

 ~MyString()
 {
  delete[]buf_;
  buf_ = nullptr;
 }

private:
 char* buf_;
 size_t len_;
};
테스트 프로그램 보기

#include "MyString.h"

int _tmain(int argc, _TCHAR* argv[])
{
 MyString str1("hello~~");
 MyString str2 = str1;
 MyString str3 = str1;

 std::cout << "str1=" << str1 << ", str2=" << str2 << ", str3=" << str3;

 return 0;
}
출력 내용 은 다음 과 같 습 니 다:

이 를 통 해 알 수 있 듯 이 세 개의 MyString 대상 을 정 의 했 습 니 다.str 2 와 str 3 는 모두 str 1 복사 구조 로 되 어 있 고 프로그램 이 실행 되 는 과정 에서 str 2 와 str 3 의 내용 은 수정 되 지 않 았 으 나 str 1 과 str 2 는 str 1 버퍼 의 내용 을 자신의 버퍼 에 복사 하 였 습 니 다.사실은 여기 서 최적화 할 수 있다.즉,str 1 과 str 2 가 구 조 를 복사 할 때 str 1 의 메모 리 를 직접 가리 키 면 중복 되 는 메모리 복사 가 피 할 수 있다.그러나 이렇게 하면 또 새로운 문 제 를 일 으 킬 수 있다.
1.여러 개의 포인터 가 같은 동적 메모 리 를 가리 키 고 있 습 니 다.메모 리 는 언제 풀 립 니까?누가 석방 합 니까?
2.대상 이 문자열 의 내용 을 수정 해 야 한다 면 예 를 들 어 처리 해 야 합 니까?
이러한 문 제 를 해결 하려 면 C++에서 비교적 전형 적 인 방안 이 두 가지 있 는데 그것 이 바로 인용 계수 와 Copy On Write 이다.
인용 계수 에서 모든 대상 은 대상 이 인용 한 모든 계수 값 을 유지 합 니 다.새로운 인용 이 대상 을 가리 킬 때 인용 계수 가 증가 하고 인용 을 없 앨 때 인용 계수 가 줄어든다.인용 계수 가 0 이 되면 이 대상 은 차지 하 는 자원 을 방출 합 니 다.
다음은 인용 계수 의 패키지 클래스 를 보 여 줍 니 다.

class RefCount
{
public:

 RefCount() : count_(new int(1)){};

 RefCount(const RefCount& other) : count_(other.count_)
 {
  ++*count_;
 }

 ~RefCount()
 {
  if (--*count_ == 0)
  {
   delete count_;
   count_ = nullptr;
  }
 }

 bool Only()
 {
  return *count_ == 1;
 }

 void ReAttach(const RefCount& other)
 {
  //           
  if (Only())
  {
   delete count_;
  }
  else
  {
   --*count_;
  }

  //            
  ++*other.count_;
  
  //          
  count_ = other.count_;
 }

 void MakeNewRef()
 {
  if (*count_ > 1)
  {
   --*count_;
   count_ = new int(1);
  }
 }

private:
 int* count_;
};
Copy On Write:글 을 쓸 때 복사 하 는 것 입 니 다.복사 구 조 를 통 해 대상 을 초기 화 할 때 매개 변수의 자원 을 새로운 대상 에 직접 복사 하지 않 고 이 자원 을 수정 해 야 할 때 기 존 자원 을 복사 한 다음 에 수정 하면 불필요 한 내 장 된 복사 가 피 할 수 있 습 니 다.
다음 코드 는 완전한 구문 류 MyString Handle 입 니 다.모든 핸들 류 는 인용 계수 의 클래스 를 포함 하여 MyString 대상 에 대한 인용 횟수 를 관리 하고 기록 합 니 다.

class MyStringHandle
{
public:
 MyStringHandle() : pstr_(new MyString){}

 //                   MyString    
 MyStringHandle(const char* str) : pstr_(new MyString(str)) {}
 MyStringHandle(const MyString& other) : pstr_(new MyString(other)) {}

 //       ,              ,          ,                   
 MyStringHandle(const MyStringHandle& ohter) : ref_count_(ohter.ref_count_), pstr_(ohter.pstr_) {}

 ~MyStringHandle()
 {
  if (ref_count_.Only())
  {
   delete pstr_;
   pstr_ = nullptr;
  }
 }

 MyStringHandle& operator=(const MyStringHandle& other)
 {
  //                 ,    
  if (other.pstr_ == pstr_)
  {
   return *this;
  }

  //        ,        MyString
  if (ref_count_.Only())
  {
   delete pstr_;
  }

  //                
  ref_count_.ReAttach(other.ref_count_);
  pstr_ = other.pstr_;

  return *this;
 }

 // str = "abc"               ,
 MyStringHandle& operator=(const char* str)
 {
  if (ref_count_.Only())
  {
   //        MyString        ,              
   *pstr_ = str;
  }
  else
  {
   //         ,       -1,        ,        MyString  
   ref_count_.MakeNewRef();
   pstr_ = new MyString(str);
  }

  return *this;
 }

private:
 MyString* pstr_;
 RefCount ref_count_;
};
테스트 프로그램 보기:

int _tmain(int argc, _TCHAR* argv[])
{
 //   MyString
 MyStringHandle str1("hello~~");

 //       MyString
 MyStringHandle str2 = str1;
 MyStringHandle str3 = str1;
 MyStringHandle str4 = str1;

 //       MyString
 MyStringHandle str5;

 //  str1   str5,       
 str5 = str1;

 //   str5  
 str5 = "123";
 str5 = "456";

 return 0;
}

총결산
이 글 은 C++문형 류 의 디자인 사상 과 간단 한 실현 을 소개 했다.주로 인용 계수 와 Copy On Write 를 통 해 이 루어 진다.이 두 가지 사상 은 매우 전형 적 이 고 쓰레기 회수,스마트 포인터 의 실현 은 모두 이 두 가지 사상 을 참고 한다.수준 이 제한 되 어 있어 오류 나 설명 이 명확 하지 않 을 수 있 습 니 다.벽돌 찍 기 를 환영 합 니 다~~

좋은 웹페이지 즐겨찾기