반응형
반응형

0. 요약

  1. 복사 생성자가 필요없으면 아래 방법을 사용하자
  2. private로 복사 생성자 / 대입 연산자를 정의
  3. 정의를 고의로 빼서 링킹 에러 발생
  4. boost 의 noncopyable 클래스를 상속받아 구현

1. 소개

  1. 부동산 클래스가 있다.
class HomeForSale{...};
  1. 모든 자산은 하나 밖에 없으니 객체 복사가 안되도록 하자
HomeForSale h1;
HomeForSale h2;
HomeForSale h3(h1); // 이게 안되도록 하자!
h1 = h2; // 이것도 안되게 하자.

2. 객체 복사 막는 방법1(외부 호출 금지)

  1. private로 복사 생성자/대입 연산자를 정의
    • 컴파일러가 자동생성을 하지 않는다.
    • 외부로부터 호출을 차단할 수 있다.
    • 하지만 그 클래스의 멤버 함수와 friend 함수가 호출 가능하다!

3. 객체 복사 막는 방법2(링킹 에러)

  1. 정의를 안해버려서 링킹 에러를 발생 시키자
    • 사용자가 실수로 복사 생성자를 호출하면 링킹에러가 발생되어 방지 가능
    • c++의 iostream(ios_base, basic_ios, sentry)가 이렇게 구현됨
class HomeForSale{
private:
   HomeForSale(const HomeForSale&)  // 선언만 있음
   HomeForSale& operator=(const HomeForSale&);
}

4. 객체 복사 막는 방법3(컴파일 에러)

  1. 링킹에러도 싫다. 컴파일 에러를 발생하자!
    • 복사 생성자, 대입 연산자를 private로 하는 기본 클래스를 생성!
    • 기본 클래스를 private상속 받아 구현!
    • 복사를 시도하면 기본 클래스의 복사를 호출
    • 하지만 기본 클래스가 private이기 때문에 컴파일 에러 발생!
    • 부스트 라이브러리 noncopyable 클래스가 동일 내용!
class Uncopyable{
protected:
   Uncopyable() {}
   ~Uncopyable(){}
private:
   Uncopyable(const Uncopyable&);
   Uncopyable& operator=(const Uncopyable&);
};

class HomeForSale: private Uncopable{...}

참고

  1. Effective C++
반응형
반응형

0. 요약

  1. 컴파일러는 기본 생성자, 복사 생성자, 복사 대입 연산자, 소멸자를 암시적으로 만들 수 있다.

1. 컴파일러가 알아서 선언하는 멤버 함수

  1. 빈클래스가 선언되어 있으면 컴파일러는 아래 멤버 함수를 자동으로 생성한다.
    • 복사 생성자(copy constructor)
    • 복사 대입 연산자(copy assignment operator)
    • 소멸자(destructor)
    • 기본 생성자 : 생성자가 없을 때 생성됨
  2. 자동 생성된 함수는 public, inline함수이다.
class Empty{};
// 위와 같이 선언하면 아래와 같다.
class Empty{
public:
   Empty() {...}                            // 기본 생성자
   Empty(const Empty& rhs) {...}            // 복사 생성자
   ~Empty() {...}                           // 소멸자
   Empty& operator=(const Empty& rhs) {...} // 복사 대입 연산자
}
  1. 위 함수가 만들어지는 조건

    • Empty e1; => 기본 생성자, 소멸자 호출
    • Empty e2(e1); => 복사 생성자 호출
    • e2 = e1 => 복사 대입 연산자 호출
  2. 소멸자

    • 상속한 기본 클래스의 소멸자가 가상이 아니면 비가상으로 생성됨
  3. 복사 생성자 자동 생성

    • 아래 예는 복사 생성자가 자동 생성됨
    • string은 자체 복사생성자가 있으므로, no2의 nameValue는 no1의 nameValue값을 가짐
    • int형 no2의 objectValue는 no1의 비트를 그대로 복사해옴
template<typename T>
class NamedObject{
public:
   NamedObject(const char* name, const T& value);
   NamedObject(const std::string& name, const T& value);
private:
   std::string nameValue;
   T objectValue;
};

// 사용 부
NamedObject<int> no1("Smallest Prime Number", 2);
NmaedObject<int> no2(no1); // 복사 생성자 자동 생성됨
  1. 복사 대입 연산자가 생성되지 않는 예
    • 최종 결과 코드가 적법하지 않거나 resonable하지 않으면 자동 생성하지 않음
    • 아래 예시에서 자동생성되지 않고 컴파일 거부됨.
    • 참조자를 데이터 멤버로 가지고 있으면, 직접 복사 대입 연산자 정의 필요!
template<class T>
class NamedObject{
public:
   NamedObject(std::string& name, const T& value);
private:
   std::string& nameValue; // 참조자
   const T objectValue;    // 상수 멤버
};

// 사용 부
std::string newDog("Persephone");
std::string oldDog("Satch");

NamedObject<int> p(newDog, 2);
NamedObject<int> s(oldDog, 36);

p = s;   // 가능한가?

참고

  1. Effective C++
반응형
반응형

0. 요약

  1. 기본 제공 타입의 객체는 직접 초기화.
    • 경우에 따라 자동으로 되기도하고, 안되기도 함
  2. 멤버 초기화리스트를 이용해 초기화하자!
    • 초기화리스트는 선언순서대로 하자!
  3. 여러 번역 단위에 있는 비지역 정적 객체들의 초기화 순서문제는 피해서 설계하자!
    • 비지역 정적객체를 지역 정적객체로 바꾸면 됨!

1. 대입과 초기화의 차이

  1. 아래는 초기화가 아니라 대입!!
    • 생성자 전에 초기화 되었고, 대입된다.
ABEntry::ABEntry(const std::string& _name, const std::string& _address){
   this.theName = name;
   this.theAddress = address;
   this.numTimes = 0;
}
  1. 아래가 초기화
    • 멤버 초기화 리스트 사용
ABEntry::ABEntry(const std::string& _name, const std::string& _address)
   :theName(_name),
   theAddress(_address),
   numTimes(0)
{}
  1. 초기화가 대입보다 더 효율적

    • 대입의 경우 초기화 후 바로 대입하므로, 앞의 초기화는 쓸모없는 행위가 됨

2. 초기화 규칙

  1. 기본 제공 타입은 멤버 초기화 리스트에 넣는 쪽으로 습관을 들이자!
    • 매개 변수가 없더라도 0으로 초기화 하는 습관!
    • 특히 상수, 참조자 형태의 데이터 멤버는 반드시 초기화 필요!
ABEntry::ABEntry()
   : theName(),
   theAddress(),
   numTimes(0)

3. 생성자가 많을 때 팁

  1. 여러 생성자에서 초기화 리스트를 정의하면 불편함
  2. private 멤버 함수 하나를 만들어 초기화 하고, 모든 생성자에서 이 함수를 호출
    • 데이터 초기값을 파일에서 읽거나,
    • 데이터베이스에서 찾아오는 경우에 유용하게 사용 가능
  3. 하지만 이런 경우가 아니면 초기화 리스트가 효율적임

4. 초기화 순서

  1. 기본 클래스는 파생 클래스보다 먼저 초기화
  2. 클래스 데이터 멤버는 선언된 순서대로 초기화
    • 초기화 리스트에서 순서가 달라져도 선언 순서대로 초기화
    • 헷갈림 방지를 위해 초기화 리스트는 선언 순서와 맞추자!

5. 비지역 정적 객체의 초기화 순서

  1. 비지역 정적 객체의 초기화 순서

    • 별개의 번역 단위에 정의된 비지역 정적 객체들의 초기화 순서는 '정해져 있지 않다'
  2. 정적 객체란?

    • 생성된 시점부터 프로그램 끝날 때 까지 살아 있는 객체
    • 정적 객체는 프로그램이 끝날 때 자동으로 소멸됨
    • 아래 에서 4)를 지역 정적 객체, 4)를 제외한 나버지를 비지역 정적 객체라고 함
  3. 정적 객체 종류

    • 1)전역 객체
    • 2)네임스페이스 유효범위에서 정의된 객체
    • 3)클래스 안에서 static으로 선언된 객체
    • 4)함수 안에서 static으로 선언된 객체
    • 5)파일 유효점위에서 static으로 정의된 객체
  4. 번역 단위란?

    • 컴파일을 통해 하나의 목적파일을 만드는 바탕이 되는 소스 코드
    • 기본적으로는 소스파일 하나며, 그 파일이 #include하는 파일까지 합쳐서 하나의 번역 단위가 됨
  5. 아래의 경우에 문제 발생

    • 별도로 컴파일된 소스 파일이 두 개 이상 있고,
    • 각 소스 파일에 비지역 정적 객체가 한 개 이상 있을 경우
  6. 문제 발생 이유

    • 한쪽 번역 단위에 있는 비정적 객체가 초기화 되면서, 다른 쪽 번역 단위에 잇는 비지역 정적 객체를 사용하는데, 객체가 초기화 되어 있지 않을 수 있다!
  7. 예제

    • tfs가 tempDir보다 먼저 초기화되어 있지 않으면, 에러 발생
// 객체 선언부
class FileSystem{
public:
   std::size_t numDisks() const;
};

extern FileSystem tfs;

// 객체 사용부(다른 파일)
class Directory{
public:
   Directory(params);
};
Directory::Directory(params){
   std::size_t disks = tfs.numDisks();
}

// 실 사용부
Directory tempDir(params);
  1. 해결 방법
    • tfs가 tempDir보다 먼저 초기화 되게는 할 수 없다.
    • 설계적인 방법(Singleton)으로 해결!!
    • 비지역 정적 객체를 하나씩 맡는 함수 선언
    • 그 함수에서 객체 참조자를 반환
    • 사용자 쪽에서는 함수호출을 하여 사용
    • 비지역 정적 객체가 지역 정적 객체로 변경됨!
class FileSystem{...};
FileSystem& tfs(){
   static FileSystem fs;
   return fs;
}

class Directory{...};
Directory::Directory(params){
   std::size_t disks = tfs().numDisks();
}

Directory& tempDir(){
   static Directory td;
   return td;
}

참고

  1. Effective C++
반응형
반응형

0. 요약

  1. const는 무조건 쓰고 보자.
    • const 함수, 함수의 매개변수(인자,리턴) 등
  2. 포인터에서 * 기호 기준 const의 위치로 상수 대상 판단
  3. mutable 키워드
    • 상수 멤버 함수에서 멤버 변수 변경 필요 시 사용
    • 가급적이면 사용하지 말자
  4. 상수 멤버 & 비상수 멤버 코드 중복 피하는 방법
    • 비상수 멤버함수에서 Casting 두 번 하여 상수 멤버를 호출하자

1. const 기초

  1. const의 장점
    • 어떤 값이 불가능 해야 한다는 개발자의 의도를 컴파일러 및 다른 프로그래머와 나눌 수 있다.
  2. 포인터에서 const 이해
    • 포인터가 가리키는 대상이 상수 : const가 * 왼쪽에 위치
    • 포인터가 상수 : const가 * 오른쪽에 위치
char greeting[] = "Hello";

char *p = greeting;              // 비상수 포인터, 비상수 데이터
const char *p = greeting;         // 비상수 포인터,    상수 데이터
char * const p = greeting;        //    상수 포인터, 비상수 데이터
const char * const p = greeting;   //   상수 포인터,     상수 데이터
  1. 포인터가 가리키는 대상을 상수로 정의하는 스타일
// 아래 둘 다 pw가 가리키는 대상을 상수로 정의
void f1(const Widget *pw);
void f2(Widget const *pw);

2. STL iterator에서 const

// iter 는 T* const 처럼 동작
const std::vector<int>::iterator iter = vec.begin();
*iter= 10;  //(O) => 가리키는 대상 변경 가능
++iter;     //(X) => 포인터 위치 변경 불가

// cIter는 const T* 처럼 동작
std::vector<int>::const_iterator cIter = vec.begin();
*cIter = 10;   //(X) => 가리키는 대상 변경 불가
++cIter;       //(O) => 포인터 위치 변경 가능

3. 함수 선언에서 const

  1. 함수 반환값을 const로 정의하는 경우
class Rational {...};
const Rational operator*(const Rational& lhs, const Rational& rhs);

// const로 정의하지 않으면,
Rational a,b,c;
// 사용자가 오타가 났을 뿐인데,
if ((a * b) = c) // a*b의 결과에 operator= 호출 가능해짐.
  1. 매개변수에 가능한한 const를 항상 사용!

4. 상수 멤버 함수

  1. 멤버 함수에 붙는 const의 역할?
    • 해당 멤버 함수가 상수 객체에 대해 호출될 함수임을 알려줌
  2. 상수 멤버 함수가 중요한 이유
    • 객체를 변경 가능한 함수와 아닌 함수를 알려줄 수 있음
    • 상수 객체를 사용할 수 있게 하자
    • C++ 성능 향상 핵심 기법 중 하나가 '객체 전달을 reference-to-const로 진행하자' 이기 때문에 상수 객체를 많이 써야함
  3. 상수 멤버 함수 사용 예제
    • operator[]의 반환값은 char& 임을 주의
    • 만약 char 라면 tb[0] = 'x' 는 컴파일 에러 발생함
class TextBlock {
public:
   //const 멤버 함수
   const char& operator[](std::size_t position) const{
      return text[position];
   }
   //비const 멤버 함수
   char& operator[](std::size_t position){
      return text[position];
   }
private:
   std::string text;
}

// 상수 객체 생성
void print(const TextBlock& ctb){
   std::cout << ctb[0]; // 상수 멤버 함수 operator[] 호출
   }


// 비상수 vs 상수 함수
TextBlock tb("Hello");
TextBlock ctb("Hello");

std::cout << tb[0];   // (O) => 비상수 멤버 함수 호출
std::cout << ctb[0]; // (O) => 상수 멤버 함수 호출
tb[0] = 'x'; // (O) => 비상수 멤버 함수 호출
ctb[0] = 'x'; // (X) => 비상수 멤버 함수 호출
// 반환값이 const char& 이기 때문에 값 할당 안됨
  1. 상수 멤버 함수의 의미
    • 비트 수준의 상수성(물리적 상수성)
    • 논리적 상수성

5. 비트 수준의 상수성(bitwise constness), 물리적 상수성(physical constness)

  1. const 멤버 함수는 객체의 멤버 변수를 건드리지 않는다.
    • 객체를 구성하는 비트들 중 어떤 것도 변경하지 않는다.
  2. 멤버 변수가 포인터인 경우 const의 역할을 잘 하지 못하는 경우 존재
    • 실제 비트 수준의 상수성은 통과함
    • 하지만 포인터를 통해 멤버 변수가 변경될 수 있음
    • 예제
class CTextBlock {
public:
   char& operator[](std::size_t position) const{
      return pText[position];
   }
private:
   char* pText;
};
// 개발자는 변수를 허락하지 않는 의도로 상수 객체 생성
const CTextBlock cctb("Hello");
char* pc = &cctb[0];
// 하지만 변경됨
*pc = 'J';

6. 논리적 상수성(logical constness)

  1. const 멤버 함수는 멤버 변수 일부는 변경 가능하지만, 사용자 측에서만 모르게 하자.
  2. mutable 키워드를 이용
class CTextBlock{
public:
   std::sizt_t length() const;
private:
   char* pText;
   mutable std::size_t textlength;
   mutable bool lengthIsVaild;
};
std::size_t CTextBlock::length() const{
   if(!lengthIsValid){
      textLength = std::strlen(pText); // 변경 가능함
      lengthIsVaild = true;   // 변경 가능함
   }

   return textlength;
}

7. 상수 멤버 & 비상수 멤버 함수에서 코드 중복 피하는 방법

  1. 비상수 멤버 함수가 상수 버전을 호출하도록 구현
    • 두 번 캐스팅 수행
    • this에 const를 붙이는 casting(static_cast)
    • const를 제거하는 casting(const_cast)
  2. static_cast<const TextBlock&>(*this)[position]
    • *this 를 const 객체로 변환
    • 변한 후 const 멤버 함수 호출(operator[] const)
  3. const_cast<char&>()
    • 위에서 반환된 const char& 를 char&로 변환
class TextBlock{
public:
   const char& operator[](std::size_t position) const{
      return text[position];
   }

   char& operator[](std::size_t position) {
      return const_cast<char&>(
         static_cast<const TextBlock&>(*this)[position]
      );
   }
}
  1. 상수 버전에서 비상수 멤버를 호출하게는 안되나요?
    • 상수 멤버 함수는 멤버 변수를 변경하지 않겠다고 약속됨
    • 따라서 비상수 멤버를 호출하면 안됨

참고

  1. Effective C++
반응형
반응형

0. 요약

  1. #define 보다는 const, enum, inline을 사용하자!

    • 가급적 선행 처리자보다 컴파일러를 더 가까이 하자!
  2. 상수 포인터 정의시 const + const(포인터 및 데이터) 사용하자!

  3. 클래스 멤버로 상수를 정의하면 정적 멤버로 정의하자!

    • static const~~

1. #difine을 쓰면 안되는 이유

#define ASPECT_RATIO 1.653
  1. ASPECT_RATIO는 컴파일 되기 전에 1.653이라는 숫자로 변경됨
  2. 따라서 컴파일러는 ASPECT_RATIO를 모름
    • 컴파일러가 쓰는 기호 테이블에 포함안됨
  3. 매크로 변경 지점에서 컴파일 에러 발생 시 디버깅 어려움
  4. 클래스 상수를 #define으로 만들 수 없다.
    • 캡슐화가 안되고, private 성격의 #define은 없다.

2. 상수를 써서 해결 하라!

const double AspectRatio = 1.653;
  1. AspectRatio는 컴파일러가 알고 있음
  2. 컴파일을 거친 최종 코드 크기가 #define 썻을 때 보다 작음
    • 상수가 부동소수점 실수 타입일 경우
    • 매크로를 쓰면 ASPECT_RATIO가 모두 1.653으로 변경되지만
    • 상수 AspectRatio는 사본이 한개만 존재

3. #define을 상수로 교체할 때 주의할 점

  1. 상수 포인터를 정의하는 경우
    • 상수 정의는 헤더 파일에 넣는 것이 상례
    • 포인터와 가리키는 대상 모두 const로 선언
// 문자열 포인터로 정의하는 경우
const char* const authorName = "Scott Meyers";
// string으로 정의하는 경우
const std::string authorName("Scott Meyers");
  1. 클래스 멤버로 상수(클래스 상수)를 정의하는 경우
    • 정적 멤버(static)로 생성
class GamePlayer {
private:
    static const int NumTurns = 5;
    int scores[NumTurns];
};

//1. 여기서 NumTurns는 '선언' 된것이다. '정의'가 아님에 주의
//2. 만약 별도의 정의가 필요할 경우 정의 제공 필요
const int GamePlayer::Numturns;
//3. 클래스 상수 정의는 헤더파일에 두지 않고 구현파일에 둔다.
//    - 클래스 상수의 초기값은 선언 시점에서 주어지기 때문
  1. 컴파일이 위의 문법을 허용하지 않을 때
    • 오래된 컴파일러에서 발생
    • 정적 클래스 멤버가 선언될 때 초기값 설정 허용 안함
    • 이 경우 초기값을 상수 정의 시점에 준다.
//헤더 파일에 둔다.
class CostEstimate {
private:
    static const double FudgeFactor; // 정의
};

//구현 파일에 둔다.
const double CostEstimate::FudgeFactor = 1.35; // 선언
  1. 클래스를 컴파일 시 클래스 상수값이 필요한 경우
    • 예)GamePlayer::scores 배열 멤버 선언 필요 시
    • enum hack 기법 사용
class gamePlayer{
private:
   enum { NumTurns = 5};
   int scores[NumTurns];
}
  1. enum hack(나열자 둔갑술)
    • const보다는 #define에 가까움
    • const의 주소는 알 수 있지만, enum의 주소는 취할 수 없다.
    • 선언한 정수 상수의 주소를 다른 사람이 알 수 없게 하려면 enum을 써라!

4. 매크로 함수

  1. 매크로 함수의 단점
    • 매크로 인자 마다 반드시 괄호를 씌워야함
    • 괄호가 있어도 예상 불가한 현상 발생 가능(아래 예시 참고)
#define CALL_WITH_MAX(a,b) f((a) > (B) ? (a) : (b))

int a = 5, b = 0;
CALL_WITH_MAX(++a, b);
// a가 두번 증가함
// 비교할 때 (a)에서 한번, 참인 경우 (a)에서 한번
CALL_WITH_MAX(++a, b+10);
// a가 한번 증가함
  1. inline 함수를 이용해 해결
    • 괄호를 매번 쓰지 않아도 되고, 인자가 여러번 평가되지 않음
    • callWithMax는 진짜 함수이기 때문에 유효 범위 및 접근 규칙을 그대로 따라감
template<typename T>
inline void callWithMax(const T& a, const T& b){
   f(a > b ? a : b);
}

참고

  1. Effective C++
반응형
반응형

0. 요약

  1. C++을 4가지 하위 언어의 엽합체로 이해하자!

    • C, 객체 지향 C++, 템플릿 C++, STL C++
  2. 사용자 정의 타입의 경우 상수 객체 참조자 전달을 사용하자!

    • const + &

1. C++ 역사?

  1. 초창기에는 C에 객체 지향 기능 몇가지가 결합된 형태
    • 클래스를 쓰는 C 였음
  2. 그 후 C++은 꾸준한 성장
    • 예외 : 함수 구성방식의 헤게모니를 크게 변경
    • 템플릿 : 프로그램 설계에 대한 새로운 사고 방식 제공
    • STL : 확장성에 대한 지평을 염
  3. 오늘 날의 C++
    • 다중패러다임 프로그래밍 언어(multiparadigm programming language)
    • 절차적 프로그래밍 기본 장착
    • 객체 지향 프로그래밍
    • 함수식 프로그래밍
    • 일반화 프로그래밍
    • 메타 프로그래밍

2. C++을 어떻게 이해 해야 하는가?

  1. 상관관계가 있는 여러 언어들의 연합체로 보라!
    • 여러 개의 하위 언어들이 C++을 이루고 있다!
  2. 그 후 각 언어에 대한 규칙을 각개 격파!

3. C++를 구성하는 하위 언어 4가지

  1. C
    • C++은 C를 기본으로 함(블록, 문장, 선행 처리자, 기본제공 데이터 타입, 배열, 포인터 등)
  2. 객체 지향 개념의 C++
    • 클래스를 쓰는 C에 관한 모든 것이 여기에 해당
  3. 템플릿 C++
    • C++의 일반화 프로그래밍 부분
    • 템플릿 메타프로그래밍이라는 새로운 패러다임 파생됨
  4. STL
    • 템플릿 라이브러리

4. 효과적인 프로그램 개발을 위한 예

  1. 한 하위 언어에서 다른 하위 언어로 옮길 때 당황하지 마라!
  2. C스타일
    • 값 전달이 참조 전달보다 효율이 더 좋다
  3. 객체 지향 C++ 과 템플릿 C++
    • 사용자 정의 생성자/소멸자 개념이 생김
    • 상수 객체 참조자에 의한 전달 방식이 더 효율이 좋음
  4. STL C++
    • 반복자와 함수 객체는 C의 포인터를 본떠 만든 것
    • 반복자 및 함수 객체에 대해 값 전달이 효율이 더 좋음

참고

  1. Effective C++
반응형
반응형

1. string의 주요 인터페이스와 특징

  1. 시퀀스 컨테이너, 배열 기반 컨테이너

  2. string, wstring 제공

    • string : char 형식 문자 관리
    • wstring : wchar_t 형식 문자 관리
  3. 멤버 정의 형식

    멤버 정의 형식 내용
    allocator_type 메모리 관리자 형식
    const_iterator const 반복자 형식
    const_pointer const value_type* 형식
    const_reference const vluae_type& 형식
    const_reverse_iterator const 역 반복자 형식
    difference_type 두 반복자 차이의 형식
    iterator 반복자 형식
    npos 찾기 관련 '실패' 정의 값, 일반적으로 -1
    pointer value_type* 형식
    reference value_type& 형식
    reverse_iterator 역 반복자 형식
    size_type 첨자나 원소 개수 등 형식
    traits_type 문자의 특성 형식
    value_type 원소의 형식
  4. 생성자

string s;              // 기본 생성자
string s(sz);          // sz 문자열로 s 생성
string s(sz,n);        // sz 문자열에서 n개 문자로 s 생성
string s(n,c);         // n개의 c 문자로 s 생성
string s(iter1,iter2); // 반복자 구간 [iter1,iter2)의 문자로 s 생성
string s(p1, p2);      // 포인터 구간 [p1, p2)의 문자로 s 생성
  1. 멤버 함수
s.append(sz)        // s에 sz를 붙임
s.assign(sz)        // s에 sz문자열을 할당
s.at(i)             // s의 i번째 문자를 참조
p=s.begin()         // p는 s의 첫문자를 가리키는 반복자
p=s.end()           // p는 s의 끝을 표식하는 반복자
p=s.rbegin()        // p는 s 역순차열의 첫문자를 가리키는 반복자
p=s.rend()          // p는 s 역순차열의 끝을 표식하는 반복자
s.c_str()           // C 스타일의 문자열 주소 반환(null문자 포함)
n=s.capacity()      // n은 s에 할당된 메모리 공간 크기
s.clear()           // s를 비움
s.compare(s2)       // s와 s2를 비교
s.copy(buf,n)       // buf로 n 개의 문자를 복사
s.data()            // 문자열의 배열 주소를 반환
s.empty()           // s가 비었는지 조사
q=s.erase(p)        // p가 가리키는 문자를 제거, q는 다음 원소 가리킴
q=s.erase(b,e)      // 반복자 구간 [b,e)의 모든 문자 제거, q는 다음 원소
s.find(c)           // c 문자를 검색
s.rfind(c)          // c 문자를 끝부터 찾음
s.insert(n,sz)      // n의 위치에 sz를 삽입
s.length()          // 문자의 개수
n=s.max_size()      // n은 s가 담을 수 있는 최대 문자 개수(메모리 크기)
s.push_back(c)      // s의 끝에 c를 추가
s.replace(pos,n,sz) // pos 위치의 n개 문자를 sz로 바꿈
s.reserve(n)        // b개의 문자를 저장할 공간 예약
s.resize(n)         // s의 크기를 n으로 변경하고 확장되는 공간의 값을 기본값으로 초기화
s.resize(n,c)       // s의 크기를 n으로 변경하고 확장되는 공간의 값을 c로 초기화
s.size()            // s의 원소 개수
s2=s.substr(pos)    // s2는 pos부터 s의 문자열
s.swap(s2)          // s와 s2를 swap
  1. 연산자

    연산자 내용
    s[i] i 번째 위치 문자
    s+=s2 s와 s2의 합을 s에 할당
    s+s2 s와 s2를 합한 string 객체
    s=s2 s2에 s 할당
    out<<s s를 스트림에 씀
    in>>s 스트림에서 s로 읽음
    getline(in,s) 스트림에서 s로 한줄을 읽음
    그 외 비교연산 ==, !=, <, >, <=, >=

참고

2. 상세 예제

  1. 문자열 초기화 예제
string t("Hello!");
const char* p1 = "Hello!";
const char* p2 = p1 + 6;

string s1;
string s2("Hello!");
string s3("Hello!", 2);
string s4(5, "H");
string s5(t.begin(), t.end());
string s6(p1, p2);
// s1 =
// s2 = Hello!
// s3 = He
// s4 = HHHHH
// s5 = Hello!
// s6 = Hello!
  1. 문자열 붙이기 예제
    • append : 부분 또는 전체 붙일 때
    • += : 전체 붙일 때
string s("He");
string t("llo!");
const char* p1 = "llo!";
const char* p2 = p1+4;

s.append(t);
// Hello!
s.append(t,0,4);
// Hello! ( [0,4) 구간 합치기 )
s.append("llo!");
// Hello!
s.append("llo!", 4);
// Hello!
s.append(t.begin(), t.end());
// Hello!
s.append(p1,p2);
// Hello!
s.append(5, 'H');
// HeHHHHH
s += t;
// Hello!
s += "llo!"
// Hello!
for(string::iterator iter=t.begin(); iter != t.end(); ++iter){
   s.push_back(*iter);
}
// Hello!
  1. 문자열 대입 예제
    • assign : 부분 또는 전체 문자열 할당
    • = : 전체 문자열 할당
string t("Hello!");
const char* p1 = "Hello!";
const char* p2 = p1 + 6;

string s;

s.assign(t);
// Hello!;
s.assign(t,0,6);
// Hello!(t의 [0,6) 구간 할당)
s.assign("Hello!");
// Hello!;
s.assing("Hello!", 6);
// Hello! ( 앞에서 부터 6개 문자열 할당 )
s.assign(6, 'H');
// HHHHHH ( H 6개 문자열 할당 )
s.assign(t.begin(), t.end());
// Hello!
s.assign(p1, p2)
// Hello!
s = t;
// Hello!
s = "Hello!";
// Hello!
  1. c_str(), data()
    • c_str : null 문자 포함한 C-style 문자열 반환
    • data : null 문자 포함하지 않는 C-style 문자열 반환
string s("Hello!");
const char *sz = s.c_str();
// \0 문자로 끝나는 문자열
const char *buf = s.data();
// \0 문자 포함하지 않는 문자열
  1. compare()
    • 문자열을 비교(부분 문자열 비교 가능)
    • s1 > s2 이면 1을 반환
    • s1 < s2 이면 -1을 반환
    • s1 == s2 이면 0을 반환
string s1("ABCDE");
string s2("AKABC");
const char* sz = "AKABC";

s1.compare(s2);
// -1 반환 ("ABCDE" 와 "AKABC" 비교)
s1.compare(2,3,s2);
// 1 반환 ( s1과 s2의 2 위치부터 3개 비교, "CDE", "ABC" 비교)
s1.compare(0,3,s2,2,3);
// 0 반환 (s1의 0 위치부터 3개["ABC"]와 s2의 2 위치부터 3개["ABC"]를 비교)
s1.compare(sz);
// -1 반환 ( "ABCDE" 와 "AKABC" 비교 )
s1.compare(2,3,sz);
// 1 반환 ( s1과 sz의 2 위치부터 3개 비교, "CDE", "ABC" 비교)
s1.compare(0,1,sz,1)
// 0 반환
  1. copy()
    • null 문자 없는 문자열 복사
string s("Hello!");
char buf[100];

s.copy(buf, s.length());
// buf = Hello!
buf[s.length()] = '\0';
// buf = Hello!\0
s.copy(buf,4,2);
// buf = llo! ( 2위치 부터 4개 복사 )
buf[4] = '\0';
// buf = llo!\0
  1. find() , rfind()
const char *sz = "Be careful in Uncle Randy's new car";
string t("Randy");
string s = sz;

s.find("e");
// 1 출력( e가 처음 발견된 위치 )
s.find("e", 10);
// 18 (10위치 부터 검색하여 e가 처음 발견된 위치)
s.find("new car");
// 28 (new car 가 처음 발견된 n의 위치)
s.find("new car", 10);
// 28 (10위치 부터 new car 가 처음 발견된 n의 위치)
s.find("new car", 0, 1);
// 12 (0 위치 부터 new car의 1개 값(n)이 처음 발견된 위치)
s.find(t, 0);
// 20 ( 0위치 부터 t("Randy")가 발견된 첫 위치)
  1. insert()
    • 문자열 삽입
string t("ABC");
string s("Hello");

s.insert(1, "ABC");
// "HABCeelo"(S의 1위치에 ABC 삽입)
s.insert(1, "ABC", 2);
// "HABeelo"(S의 1위치에 ABC에서 2개문자[AB] 삽입)
s.insert(1, t);
// "HABCello" ( s의 1위치에 t["ABC"] 삽입)
s.insert(1, t, 0, 2);
// "HABeelo" ( s의 1위치에 t의 0번째 위치 부터 2개 문자["AB"] 삽입)
s.insert(1, 3, "A");
// "HAAAello" ( s의 1위치에 "A"를 3개 삽입)
s.insert(s.begin() + 1);
// "H ello" ( s의 1위치에 공백 삽입 )
s.insert(s.begin() + 1, 'A');
// "HAello" ( s의 1위치에 'A' 삽입)
s.insert(s.begin() + 1, 3, 'A');
// "HAAAello" ( s의 1위치에 'A'를 3개 삽입)
s.insert(s.begin() + 1, t.begin(), t.end());
// "HABCello" ( s의 1위치에 t[begin,end) 삽입)
  1. replace()

    • 문자열을 교체
string t("ABC");
string s("Hello!");

s.replace(0,3,"ABC");
// "ABClo!" (s의 0번째 부터 3개 문자를 "ABC" 로 변경)
s.replace(0,3,t);
// "ABClo!" (s의 0번째부터 3개 문자를 t["ABC"]로 변경)
s.replace(0,3,"ABC",2);
// "ABlo! ( s의 0번째부터 3개 문자를 "ABC"의 앞 2개 문자로 변경)
s.replace(0,3,t,0,2);
// "ABlo! ( s의 0번째부터 3개 문자를 t의 0번째부터 2개 문자["AB"]로 변경)
s.replace(0,3,2,'A');
// "AAlo!" ( s의 0번째부터 3개 문자를 'A' 2개로 변경)
s.replace(s.begin(), s.begin()+3, "ABC");
// "ABClo!" (s의 0번째 부터 3개 문자를 "ABC" 로 변경)
s.replace(s.begin(), s.begin()+3, t);
// "ABClo!" (s의 0번째부터 3개 문자를 t["ABC"]로 변경)
s.replace(s.begin(), s.begin()+3, "ABC", 2);
// "ABlo! ( s의 0번째부터 3개 문자를 "ABC"의 앞 2개 문자로 변경)
s.replace(s.begin(), s.begin()+3, 3, 'A');
// "AAAlo!" ( s의 0번째부터 3개 문자를 "A" 3개로 변경)
s.replace(s.begin(), s.end(), t.begin(), t.end());
// "ABC" (s의 문자를 t의 [begin,end) 구간 문자["ABC"]로 변경)
  1. substr()
  • 일부 문자열을 추출할 때 사용
string t("Hello!");
string s;

s = t.substr(0);
// "Hello!" ( 0 부터 끝까지 )
s = t.substr(0, string::npos);
// "Hello!" ( 0 부터 끝까지 )
s = t.substr(0, 2);
// "He" ( 0부터 2개 )
s = t.substr(2,3);
// "llo" ( 2부터 3개 )
s = t.substr(2, string::npos);
// "llo!" ( 2부터 끝까지)
  1. 스트림으로 부터 입력
string s;
getline(cin, s);
// 문자열을 입력 받는다.
getline(cin, s ,'\n');
// 문자열을 입력 받는다. 종료 문자열 저장 가능
  1. 뇌를 자극하는 C++ STL
반응형
반응형

1.stack

  1. 기본 컨테이너는 deque
  2. 템플릿 형식
    • T : 원소 형식
    • Conatiner : stack에 사용될 컨테이너
template<typename T,
         typename Container=deque<T>>
class stack
  1. 멤버 형식
멤버 형식 내용
value_type Container::value_type, T형식
size_type Container::size_type, 첨자 또는 원소 개수 등의 형식
container_type Container 형식, 기본 deque
  1. 생성자
explicit stack(const Container& = Container())
// 컨테이너 기본 생성자 호출해 stack을 생성하거나
//인자로 받아 stack을 생성
  1. 멤버 함수
bool empty() const             // 원소가 없는가?
size_type size() const         // 원소의 개수
void push(const vluae_type& x) // 원소 추가
void pop()                     // 원소 제거
value_type& top()              // Top 원소의 참조
const value_type& top() const  // const 객체 Top 원소 참조
  1. 예제
#include<stack>
stack<int> st;
st.push(10);   // 원소 추가
st.push(20);
st.push(30);

while(!st.empty()){
   cout<< st.top() << endl; // Top 원소 꺼내기
   st.pop(); // 원소 제거
}

2. queue 컨테이너

  1. 기본 컨테이너는 deque
  2. 템플릿 형식
    • T : 원소 형식
    • Conatiner : queue에 사용될 컨테이너
template<typename T,
         typename Container=deque<T>>
class queue
  1. 멤버 형식
멤버 형식 내용
value_type Container::value_type, T형식
size_type Container::size_type, 첨자 또는 원소 개수 등의 형식
container_type Container 형식, 기본 deque
  1. 생성자
explicit queue(const Container& = Container())
// 컨테이너 기본 생성자 호출해 queue을 생성하거나
//인자로 받아 queue를 생성
  1. 멤버 함수
bool empty() const             // 원소가 없는가?
size_type size() const         // 원소의 개수
void push(const vluae_type& x) // 원소 추가
void pop()                     // 원소 제거
value_type& front()            // 첫 원소 참조
value_type& back()              // 마지막 원소의 참조
const value_type& front() const // const 객체 첫 원소 참조
const value_type& back() const   // const 객체 마지막 원소 참조
  1. 예제
#include<queue>
queue<int, list<int>> q;
q.push(10);   // 원소 추가
q.push(20);
q.push(30);

while(!q.empty()){
   cout<< q.front() << endl; // Top 원소 꺼내기
   q.pop(); // 원소 제거
}

3. priority_queue 컨테이너

  1. 들어간 순서에 상관없이 우선순위가 높은 데이터나 나옴

  2. STL에서 priority_queue

    • STL의 힙 알고리즘(make_heap, push_heap, pop_heap)을 사용해 구현
    • 임의 접근 반복자를 제공해야함(vector 또는 deque)
  3. 기본 컨테이너는 vector

  4. 템플릿 형식

    • T : 원소 형식
    • Conatiner : priority_queue에 사용될 컨테이너, 기본 vector
    • Comp : 우선순위를 결정할 정렬 기준, 기본 less
template<typename T,
         typename Container=deque<T>,
         typename Comp=less<typename Container::value_type>>
class priority_queue
  1. 멤버 형식
멤버 형식 내용
value_type Container::value_type, T형식
size_type Container::size_type, 첨자 또는 원소 개수 등의 형식
container_type Container 형식, 기본 vector
  1. 생성자
explicit priority_queue(
   const Comp& = Comp(),
   const Container& = Container())
// 컨테이너 기본 생성자 호출해 queue을 생성하거나
//인자로 받아 priority_queue를 생성
  1. 멤버 함수
bool empty() const             // 원소가 없는가?
size_type size() const         // 원소의 개수
void push(const vluae_type& x) // 원소 추가
void pop()                     // 원소 제거
value_type& top()              // Top 원소의 참조
const value_type& top() const   // const 객체 Top 원소 참조
  1. 예제
#include<queue>
priority_queue<int> pq1;
pq1.push(40);   // 원소 추가
pq1.push(20);
pq1.push(30);
pq1.push(50);
pq1.push(10);

while(!pq1.empty()){
   cout<< pq1.top() << endl; // Top 원소 꺼내기
   // 50 40 30 20 10 (less)
   pq1.pop(); // 원소 제거
}

priority_queue<int, deque<int>, greater<int>> pq2;
pq2.push(40);   // 원소 추가
pq2.push(20);
pq2.push(30);
pq2.push(50);
pq2.push(10);

while(!pq2.empty()){
   cout<< pq2.top() << endl; // Top 원소 꺼내기
   // 10 20 30 40 50 (greater)
   pq2.pop(); // 원소 제거
}

참고

  1. 뇌를 자극하는 C++ STL
반응형

'C++ > STL' 카테고리의 다른 글

[STL-19] string  (0) 2021.03.14
[STL-17] 반복자  (0) 2021.03.14
[STL-16] 함수 객체 - 함수 어댑터  (0) 2021.03.14
[STL-15] 함수 객체 - 비교/논리 연산 조건자  (0) 2021.03.14
[STL-14] 함수 객체 - 산술 연산 함수 객체  (0) 2021.03.14
반응형

1. 반복자 개요

  1. 반복자란?
    • 포인터를 추상화한 클래스
    • 포인터가 하지 못하는 더 많은 동작 가능
  2. 반복자의 종류
    • 입력 반복자 : 전방향 읽기(istream)
    • 출력 반복자 : 전방향 쓰기(ostream)
    • 순방향 반복자 : 전방향 읽기,쓰기
    • 양방향 반복자 : 양방향 읽기, 쓰기(list, set, multiset, map, multimap)
    • 임의 접근 반복자 : 랜덤 읽기, 쓰기(vector, deque)
  3. 반복자 종류에 따른 가능 연산
반복자 가능 연산
입력 반복자 *iter, ->, ++, ==,!=, iterator(iter)
출력 반복자 *iter=x, ++, iterator(iter)
순방향 반복자 *iter, ->, ++, == , =, iterator(), iterator(iter)
양방향 반복자 순방향 반복자 기능 + --
임의 접근 반복자 양방향 반복자 기능 + , [], +=, -=, +, -, <, >, <=, >=
  1. 순차열

    • 순서 있는 원소의 집합
  2. 구간

    • 순차열의 시작과 끝을 나타내는 반복자의 쌍으로 표현
    • [begin, end) : begin은 순차열 원소에 포함되지만, end는 포함되지 않음
    • ex) 10, 20, 30, 40, 50, 60
    • iter = 30 일 경우
    • [begin, end) => 10, 20, 30, 40, 50
    • [begin, iter) => 10, 20
    • [iter, end) => 30, 40, 50

2. 반복자 상세

종류 방향 쓰기
iterator 정방향 O
const_iterator 정방향 X
reverse_iterator 역방향 O
const_ reverse_iterator 역방향 X
  1. const 키워드 + 반복자

    • 반복자가 가리키는 원소의 위치 변경 안할 때 사용
  2. 역방향 반복자

    • 반복자가 가리키는 다음 원소의 값을 참조함
    • ex) 10,20,30,40,50 일 때
    • rbegin 은 end()를 가리키고, 50을 참조
    • rend 는 10을 가리키고, 10 한칸 앞을 참조
vector<int> v = {10,20,30,40,50};
vector<int>::iterator iter = v.begin();
vector<int>::const_iterator citer = v.begin();
const vector<int>::iterator const_iter = v.begin();
const vector<int>::const_iterator const_citer = v.begin();

// 값 대입
*iter  = 100; *const_iter = 100;   // 가능
*citer = 100; *const_citer = 100;  // 불가능

// 반복자 변경
**iter; ++citer;              // 가능
++const_iter , ++const_citer; // 불가능

// 역방향 반복자
for(vector<int>::reverse_iterator riter v.rbegin(); riter != v.rend(), +riter){
   cout << *riter << " ";
}
cout << endl;
// 50,40,30,20,10 출력됨

3. 삽입 반복자

  1. 순차열에 원소를 삽입 할 수 있게 반복자를 변환하는 어댑터

    • 기본은 덮어쓰기 모드로 동작
  2. inserter()

    • insert_iterator 객체 생성
    • 컨테이너의 insert() 멤버 함수 호출
    • 삽입모드로 동작하게 함
    • 모든 컨테이너 사용 가능
vector<int> v1 = {10,20,30,40,50};
vector<int> v2;
copy(v1.begin(), v1.end(), v2.begin());
// copy 알고리즘은 덮어쓰기 모드로 동작하므로
// v2의 size가 0 이여서 오류 발생함

copy(v1.begin(), v1.end(), inserter<vector<int>>(v2, v2.begin()));
// 삽입 모드로 변경
// v2  = {10,20,30,40,50};
  1. back_inserter()

    • back_insert _iterator 객체 생성
    • 컨테이너의 push_back() 멤버 함수 호출
    • 뒤쪽에 삽입함
    • vector, deque, list 만 사용 가능
  2. front_inserter()

    • front_insert _iterator 객체 생성
    • 컨테이너의 push_front() 멤버 함수 호출
    • 앞쪽에 삽입함
    • deque, list만 사용 가능
vector<int> v = {10,20,30,40,50};
list<int> lt1 = {1,2,3};
list<int> lt2 = {1,2,3};

copy(v.begin(), v.end(), back_inserter<list<int>>(lt1));
// lt1 = {1,2,3,10,20,30,40,50}

copy(v.begin(), v.end(), front_inserter<list<int>>(lt2));
// lt2 = {50,40,30,20,10,1,2,3}

4. 입/출력 스트림 반복자

  1. istream_iterator<T>

    • 입력 스트림과 연결된 반복자
    • T 형식의 값을 스트림에서 읽을 수 있음
  2. ostream_iterator<T>

    • 출력 스트림과 연결된 반복자
    • T 형식의 값을 스트림에 쓸 수 있음
vector<int> v = {10,20,30,40,50};
copy(v.begin(), v.end(), ostream_iterator<int>(cout));
// 1020304050 이 출력됨
copy(v.begin(), v.end(), ostream_iterator<int>(cout, ","));
// 10,20,30,40,50 이 출력됨

list<int> lt = {100,200,300};
transform(
   lt.begin(), lt.end(), v.begin(),
   ostream_iterator<int>(cout, " "),
   plus<int>());
// 110 220 330 이 출력됨

//istream_iterator
vector<int> v2;
copy(
   istream_iterator<int>(cin),
   istream_iterator<int>(),
   back_inserter<vector<int>>(v));
// 사용자 입력을 받아 v 에 push_back

copy(
   istream_iterator<int>(cin),
   istream_iterator<int>(),
   ostream_iterator<int>(cout, " "));
// 사용자 입력을 받아 cout으로 출력
// ctrl + D 를 누르면 입력 종료

5. 반복자 특성과 보조 함수

  1. 반복자 특성이란?
    • 각 반복자의 특징을 저장하는 템플릿 클래스
    • 사용자 알고리즘 구현 할 때 STL 알고리즘 처럼 일반화하면서 반복자 종류의 특징에 따라 효율적인 동작을 하는 알고리즘을 구현하려면 STL이 제공하는 반복자 특성 활용 필요
  2. iterator_traits
    • 모든 반복자의 공통된 인터페이스
    • 모든 반복자가 제공하는 다섯 정보를 가지고 있음
template<class Iter>
struct iterator_traits{
   typedef typename Iter::iterator_category iterator_category
   typedef typename Iter::value_type value_type;
   typedef typename Iter::difference_type difference_type;
   typedef typename Iter::pointer pointer;
   typedef typename Iter::reference reference;
}
  1. 반복자 태그(5개)
    • 반복자의 종류를 구분하기 위해 사용
struct input_iterator_tag {...};
struct output_iterator_tag{...};
struct forward_iterator_tag
   :public input_iterator_tag{...};
struct bidirectional_iterator_tag
   :public forward_iterator_tag{...};
struct random_access_iterator_tag
   :public bidirectional_iterator_tag{...};
  1. 사용자 함수 구현 예시
//vector
template<typename T>
class Vector{
public:
   class Iterator{
      typedef random_access_iterator_tag iterator_category;
      typedef T value_type;
      typedef int difference_type;
      typedef T* pointer;
      typedef T& reference;
      void operator+=(int) { }
   }
      Iterator Begin() {
      return Iterator();
   }
}

//list
template<typename T>
class List{
public:
   class Iterator{
      typedef bidirectional_iterator_tag iterator_category;
      typedef T value_type;
      typedef int difference_type;
      typedef T* pointer;
      typedef T& reference;
      void operator++() { }
   }
   Iterator Begin() {
      return Iterator();
   }
}

// advance(양방향 반복자) 오버로딩
template<typename Iter>
void _Advance(Iter& iter,
            int n,
            bidirectional_iterator_tag category_tag){
   for(int i = 0; i < n; i++){
      ++iter;
   }
}

// advance(임의 접근 반복자) 오버로딩
template<typename Iter>
void _Advance(Iter& iter,
            int n,
            random_access_iterator_tag category_tag){
   iter += n;
}

// Advance() 반복자 보조 함수
template<typename Iter>
void Advance(Iter& iter, int n){
   _Advance(iter, n, iterator_traits<Iter>::iterator_category());
}

// 사용 예시
Vector<int> v = {10,20,30};
List<int> v = {10,20,30};

Vector<int>::Iterator viter(v.Begin());
List<int>::Iterator liter(lt.Begin());

Advance(viter, 2);
Advance(liter, 2);

6. 보조 함수

  1. advance(p, n)

    • p 반복자를 p += n의 위치로 이동
  2. n=distance(p1, p2)

    • n은 p2 - p1
    • distance(v.begin(),v.end()) = 원소 개수

참고

  1. 뇌를 자극하는 C++ STL
반응형
반응형

1. 바인더

  1. 이항 함수자를 단항 함수자로 변환
  2. STL은 두 가지 바인더 제공
    • bind1st : 첫번 째 인자 고정
    • bind2nd : 두번 째 인자 고정
  3. 예제
    • less 첫 번째 인자를 10으로 고정
   // less의 첫 인자를 10으로 고정한 binder
   binder1st<less<int>> binder = bind1st(less<int>(), 10);
   binder(5); // => less<int>()(10,5) 와 동일

   // 임시 객체로 사용
   bind1st(less<int>(), 10)(5);
  1. c++11 에서 비추 , c++17 에서 삭제됨
    • std::bind를 대신 사용하자.

2. 부정자(negator)

  1. 조건자를 반대의 조건자로 변환
  2. STL은 두 가지 부정자 제공
    • not1 : 단항 조건자를 변환
    • not2 : 이항 조건자를 변환
  3. 예제
    • less 조건자를 반대 조건자로 변환
less<int> oLess;
binary_negate<less<int>> negate = not2(less<int>());
// 아래 세 방법은 모두 동일
negate(5, 10);
not2(oLess)(5,10);
not2(less<int>())(5, 10);
// 5 < 10 의 반대는 5 => 10 이므로 false

3. 함수 포인터 어댑터

  1. 일반 함수를 어댑터 적용 가능한 함수 객체로 변환
    • 일반 함수는 STL 어댑터에 적용 안됨
    • 어댑터에 적용 가능한 함수 객체로 변환
  2. STL은 아래 함수 포인터 어댑터 제공
    • ptr_fun()
  3. 사용 예시
    • 사용자 정의 함수를 변환
bool Pred(int n){
   return 30 <= n && n <= 40;
}

vector<int> v = {10,20,30,40,50};
//30이상 40이하의 원소 개수 => 2
count_if(
   v.begin(),
   v.end(),
   Pred);

// 30이상 40이하가 아닌 원소 개수 => 3
count_if(
   v.begin(),
   v.end(),
   not1(ptr_fun(Pred)));
  1. 사용자 정의 ptr_fun 구현
template<typename RType, typename AType>
class Ptr_fun_class:public unary_function<AType, RType>{
   RType (*pf)(AType);
public:
   Ptr_fun_class(RType (*_pf)(AType))
      : pf(_pf) {}
   RType operator()(AType n) const{
      return pf(n);
   }
}

// 일반 함수를 함수 객체로 변환하는 Ptr_fun() 함수
template<typename RType, typename Atype>
Ptr_func_class<RType,AType> Ptr_fun(
   RType (*pf)(Atype) {
      return Ptr_fun_class<Rtpye, AType>(pf);
   }
)

4. 멤버 함수 포인터 어댑터

  1. 멤버 함수를 함수 객체로 변환
  2. 알고리즘이 객체 원소의 멤버 함수 호출 가능
  3. STL은 두 가지 멤버 함수 포인터 어댑터 제공
    • mem_fun_ref() : 객체로 멤버 함수 호출
    • mem_fun() : 객체의 주소로 멤버 함수 호출
  4. mem_fun_ref() 예시
vector<Point> v;
v.push_back(Point(1,1));
v.push_back(Point(2,2));
v.push_back(Point(3,3));
v.push_back(Point(4,4));
v.push_back(Point(5,5));

// 멤버 함수 호출 불가능
for_each(
   v.begin(), v.end(),
   &Point::Print);

// 멤버 함수 호출 가능
for_each(
   v.begin(), v.end(),
   mem_fun_ref(&Point::Print));
  1. 사용자 정의 Mem_fun_ref 예시
template<typename RType, typename CType>
class Mem_fun_ref_class:public unary_function<CType, RType>{
   RType (CType::*pf)() const;
public:
   Mem_fun_ref_class(RType (CType::*_pf)() const)
      : pf(_pf) {}
   RType operator()(const CType& o) const{
      return (o.*pf)();
   }
}

// 어댑터 함수 : 멤버 함수를 주소를 저장하는 함수 객체로 반환
template<typename RType, typename CType>
Mem_fun_ref_class<RType, CType> Mem_fun_ref(
   RType (CType::*pf() const){
      return Mem_fun_ref_class<RType,CType>(pf);
   }
)
  1. mem_fun() 어댑터 예시
    • 원소가 객체의 주소 일 때 사용
vector<Point*> v;
v.push_back(new Point(1,1));
v.push_back(new Point(2,2));
v.push_back(new Point(3,3));
v.push_back(new Point(4,4));
v.push_back(new Point(5,5));

for_each(
   v.begin(),
   v.end(),
   mem_fun(&Point::Print)
);

참고

  1. 뇌를 자극하는 C++ STL
반응형

'C++ > STL' 카테고리의 다른 글

[STL-18] 컨테이너 어댑터  (0) 2021.03.14
[STL-17] 반복자  (0) 2021.03.14
[STL-15] 함수 객체 - 비교/논리 연산 조건자  (0) 2021.03.14
[STL-14] 함수 객체 - 산술 연산 함수 객체  (0) 2021.03.14
[STL-13] 함수 객체 - 개요  (0) 2021.03.14

+ Recent posts

반응형