gugbab2's GitBook
  • Language
    • C++
      • 강의
        • C++ 언매니지드 프로그래밍
          • C++ 프로그래밍
          • 출력(Output)
          • 입력(Input)
          • bool 타입, Reference
          • 상수(const)
          • 문자열(string)
          • 파일 입출력
          • 개체지향 프로그래밍1
          • 개체지향 프로그래밍2
          • 개체지향 프로그래밍3
          • 캐스팅(형변환, casting)
          • 인라인 함수
          • static 키워드
          • 예외(Exception)
          • STL(Standard Template Library) 컨테이너(Container) - Vector
          • STL 컨테이너 - Map
          • STL 컨테이너 - Queue, Stack, Set, List
          • 템플릿(Template) 프로그래밍
          • 새로운 키워드(C++11 ~) 1
          • 새로운 키워드(C++11 ~) 2
          • 새로운 자료형
          • 새로운 STL 컨테이너
          • 스마트(smart) 포인터
          • 이동생성자 및 이동대입연산자
          • constexpr
          • Lamda Expression
      • 책
        • The C++ Programming Lanuaage
          • 2부 : 기본 기능
            • 6. 타입과 선언
            • 7. 포인터, 배열, 참조
            • 8. 구조체(struct), 공용체(union), 열거형(enum)
            • 10. 표현식
            • 11. 선택 연산
            • 12. 함수
            • 13. 예외 처리
            • 15. 소스 파일과 프로그램
          • 3부 : 추상화 메커니즘
            • 16. 클래스
            • 17. 생성, 소멸, 복사와 이동
            • 18. 연산자 오버로딩
            • 19. 특수 연산자
            • 20. 파생클래스
        • 씹어먹는 C++
          • 2. C++ 참조자(reference) 의 도입
          • 5.1 연산자 오버로딩(비교, 대입 연산자)
          • 5-2. 연산자 오버로딩(이항, 입출력, 타입변환, 증감 연산자)
          • 6-2. 가상(virtual) 함수와 다형성
          • 6-3. 가상 함수에 대한 지식들
          • 9-1. 코드를 찍어내는 틀 - C++ 템플릿(template)
          • 9-2. 가변 길이 템플릿(Variadic template)
          • 9-3. 템플릿 메타 프로그래밍 (Template Meta Programming)
          • 9-4. 템플릿 메타 프로그래밍2
          • 16.1 유니폼 초기화(Uniform Initialization)
          • 토막글 2. 람다(lambda)
    • Java
      • 강의
        • 김영한의 실전 자바 - 기본편
          • 절차 지향 vs 객체 지향
            • 절차 지향 프로그래밍
            • 객체 지향 프로그래밍
          • 변수
            • 클래스 변수 / 인스턴스 변수, 멤버 변수 / 지역 변수
            • 기본형 vs 참조형
          • 패키지
            • 패키지
            • CLI 환경에서 .java 파일 컴파일 && 실행
          • 접근 제어자
            • 접근 제어자 - 기본
            • 캡슐화
          • static
            • 자바 메모리 구조
            • static 기본
            • 스택 영역, 힙 영역
              • 스택 영역, 힙 영역 - 기본
              • 메소드가 실행될 때 어떤일이 일어나는가?
          • 상속
            • 상속 기본
          • 다형성(Pilymorphism)
            • 다형성 기본
            • 다형성의 활용
              • 다형성의 활용 - 기본
              • 다형성의 활용 - 추상클래스
              • 다형성의 활용 - 인터페이스
            • 다형성과 설계
              • 좋은 객체 지향 프로그래밍
        • 김영한의 실전 자바 - 중급1편
          • 1. Object 클래스
          • 2. 불변 객체
          • 3. String 클래스
          • 4. 래퍼, Class 클래스
          • 5. 열거형 - ENUM
          • 6. 날짜와 시간
          • 7. 중첩 클래스, 내부 클래스1
          • 8. 중첩 클래스, 내부 클래스2
          • 9. 예외 처리1 - 이론
          • 10. 예외 처리 - 실습
        • 김영한의 실전 자바 - 중급2편
          • 1. 제네릭 - Generic1
          • 2. 제네릭 - Generic2
          • 3. 컬렉션 프레임워크 - ArrayList
          • 4. 컬렉션 프레임워크 - LinkedList
          • 5. 컬렉션 프레임워크 - List
          • 6. 컬렉션 프레임워크 - 해시(Hash)
          • 7. 컬렉션 프레임워크 - HashSet
          • 8. 컬렉션 프레임워크 - Set
            • 레드 블랙 트리
          • 9. 컬렉션 프레임워크 - Map, Stack, Queue
            • 왜(?) Set 은 내부에서 Map 을 사용할까?
          • 10. 컬렉션 프레임워크 - 순회, 정렬, 전체 정리
        • 김영한의 실전 자바 - 고급 1편, 멀티스레드와 동시성
          • 프로세스와 스레드 소개
          • 스레드 생성과 실행
          • 스레드 제어와 생명 주기1
          • 스레드 제어와 생명 주기2
          • 메모리 가시성
          • 동기화 - synchronized
            • synchronized 키워드 이해도 체크
          • 고급 동기화 - concurrent.Lock
          • 생산자 소비자 문제1
          • 생산자 소비자 문제2
          • CAS - 동기화와 원자적 연산
          • 동시성 컬렉션
          • 스레드 풀과 Executor 프레임워크1
          • 스레드 풀과 Executor 프레임워크2
        • 김영한의 실전 자바 - 고급 2편, I/O, 네트워크, 리플렉션
          • 문자 인코딩
          • I/O 기본1
          • I/O 기본2
          • I/O 활용
          • File, Files
          • 네트워크 - 프로그램1
          • 네트워크 - 프로그램2
          • 채팅 프로그램
          • HTTP 서버 만들기
          • 리플렉션
          • 애노테이션
          • HTTP 서버 활용
        • 김영한의 실전 자바 - 고급3편, 람다, 스트림, 함형 프로그래밍
          • 람다가 필요한 이유
          • 람다
          • 함수형 인터페이스
          • 람다 활용
          • 람다 vs 익명 클래스
          • 메서드 참조
          • 스트림API1 - 기본
          • 스트림 API2 - 기능
          • 스트림 API3 - 컬렉터
          • Optional
          • 디폴트 메서드
          • 병렬 스트림
          • 함수형 프로그래밍
        • 기초 탄탄! 독하게 시작하는 Java - Part2: OOP 와 JVM
          • 2. 클래스 - 첫 번째
          • 3. 클래스 - 두번째
          • 4. 상속과 관계
          • 6. JVM(Java Virtual machine) 기본 이론
          • 7. JVM 과 GC 그리고 객체
          • 8. 불변 객체와 String 클래스
      • 책
        • 자바의 신
          • 변수
            • 클래스 변수(static) 사용 주의 케이스
            • Java volatile 과 Atomic 변수(+CAS)
          • 연산자
            • 비트 연산자 활용 예제
          • 배열
          • 참조 자료형
          • 상속
          • Object 클래스
          • interface, abstract class, enum
          • 예외
          • String 클래스
            • String 구조
            • String 문자열을 byte 로 변환하기
            • String 클래스에서 자주 사용되는 메서드
            • String 클래스로 살펴보는 불변(Immutable)객체
            • StringBuilder, StringBuffer
          • Nested 클래스
          • 어노테이션
            • 어노테이션 기본
            • 어노테이션의 사용
          • JVM 이해하기
            • 왜 JVM 을 사용해?
            • JVM, JRE, JDK
            • JVM 구조 이해하기
            • 클래스 로더 시스템
            • JIT(Just-In-Time) 컴파일러
            • GC(Garbage Collector)
              • GC Part.1
              • GC Part.2
              • GC 튜닝
          • java.lang
            • Wrapper 클래스
            • System 클래스
          • Generic
            • 제네릭 기본
            • 와일드카드
            • 와일드카드 GET / SET 경계
            • 와일드카드 extends / super 사용시기
            • 혼동할 수 있는 와일드카드 표현
          • Collection
            • 자료구조
              • 이진 탐색 트리 vs 레드 블랙 트리
            • Collection
            • List
              • ArrayList
              • Vector
              • Stack
              • LinkedList
            • Set, Queue
              • HashSet
              • LinkedHashSet
              • TreeSet
              • Priority Queue
              • ArrayDeque
            • Map
              • HashMap
              • Hashtable
              • LinkedHashMap
              • TreeMap
          • Thread
            • Thread 기본
            • Thread 와 관련이 많은, Synchronized
            • Thread 를 통제하는 메서드
            • ThreadGroup
          • I/O
            • InputStream, OutputStream
            • Reader, Writer
          • Serializable, NIO
            • Serializable
            • NIO (New IO)
          • 네트워크 프로그래밍
            • 네트워크 기본 & TCP 통신
            • UDP 통신
          • 람다
            • 함수형 인터페이스
            • 람다란?
        • 벨둥(Bealdung)
          • Java Concurrency
            • Java Concurrency Basics
              • Overview of the java.util.concurrent
              • Guide to the Synchronized Keyword in Java
              • Guide to the Volatile Keyword in Java
              • Guide to the java.util.concurrent.Future
              • ThreadLocal in Java
      • 그 외
        • 시스템 콜과 자바에서의 시스템 콜 사용례
        • 자바 NIO 의 동작원리 및 IO 모델
        • 함수형 인터페이스(FunctionInterface) - 자바8
  • Spring
    • 강의
      • 스프링 핵심 원리 - 기본편
        • 큰 흐름 잡기
        • 스프링 핵심 원리 이해1 - 예제 만들기
        • 스프링 핵심 원리 이해2 - 객체 지향 원리 적용
        • 스프링 컨테이너와 스프링 빈
        • 싱글톤 컨테이너
        • 컴포넌트 스캔
        • 의존관계 자동 주입
        • 빈 생명주기 콜백
        • 빈 스코프
      • 토비의 스프링6 - 이해와 원리
        • 3. 오브젝트와 의존관계1
        • 3. 오브젝트와 의존관계2
        • 4. 테스트
        • 5. 템플릿
        • 6.예외
        • 7. 서비스 추상화
    • 책
      • JSP 2.3 웹 프로그래밍
        • Servlet
        • JSP
        • 쿠키 / 세션
        • MVC 패턴
        • 실무 때 고민할 만한 부분
      • 스프링 입문을 위한 자바 객체지향의 원리와 이해
        • 자바와 절차적/구조적 프로그래밍
        • 객체지향의 4대 특성
        • 객체지향 설계의 5원칙
        • 스프링이 사랑한 디자인 패턴
        • IoC / DI
        • AOP(Aspect Oriented Programming), 관점 지향 프로그래밍
      • 토비의 스프링 3.1
        • Spring vs Spring Boot
        • 1. 오브젝트와 의존관계
          • 1.4 제어의 역전(IoC)
          • 1.5 스프링의 IoC
          • 1.6 싱글톤 레지스트리와 오브젝트 스코프
    • 그 외
      • 스프링 부트(SpringBoot) 탄생 배경
  • CS
    • DATA STRUCTURES
      • 선택 정렬(Selection Sort)
      • 버블 정렬(Bubble Sort)
      • 삽입 정렬(Insertion Sort)
    • OS
      • 강의
      • 책
        • 혼자 공부하는 컴퓨터구조 + 운영체제
          • 1. 컴퓨터 구조 시작하기
          • 2. 데이터
          • 3. 명령어
          • 4. CPU 의 작동원리
          • 5. CPU 성능 향상 기법
          • 6. 메모리와 캐시메모리
          • 7. 보조기억장치
          • 8. 입출력장치
          • 9. 운영체제 시작하기
          • 10. 프로세스와 스레드
    • NETWORK
      • 그 외
        • REST API
          • REST API
          • URI & MIME type
          • Collection Pattern
          • Collection Pattern 적용
          • Spring Web MVC 구현
        • SSL 인증 동작
        • DTO & JSON & CROS
          • DTO
          • 직렬화(Serialization)
          • Jackson ObjectMapper
          • CROS
        • Connection Timeout / Read Timeout
      • 강의
        • 외워서 끝내는 네트워크 핵심이론 - 기초
          • Internet 기반 네트워크 입문
            • Host 는 이렇게 외우자
            • 스위치가 하는 일과 비용
          • L2 수준에서 외울 것들
            • NIC, L2 Frame, LAN 카드 그리고 MAC 주소
            • L2 스위치에 대해서
            • LAN 과 WAN 의 경계 그리고 Broadcast
          • L3 수준에서 외울 것들
            • IPv4 주소의 기본 구조
            • L3 IP Packet 으로 외워라
            • 패킷의 생성과 전달 및 계층별 데이터 단위
            • 이해하면 인생이 바뀌는 TCP/IP 송, 수신 구조
            • IP 헤더 형식
            • 서브넷 마스크와 CIDR
            • Broadcast IP 주소와 Localhost
            • TTL 과 단편화
            • 인터넷 설정 자동화를 위한 DHCP
            • ARP 과 Ping(RTT : Round Trip Time)
          • L4 수준 대표주자 TCP 와 UDP
            • TCP 와 UDP 개요
            • TCP 연결 및 상태 변화
            • TCP 연결 종료 및 상태 변화
            • TCP, UDP 헤더 형식과 게임서버 특징
            • TCP 가 연결이라는 착각
            • TCP 연결과 게임버그
          • 웹을 이루는 핵심기술
            • DNS
            • URL, URI
        • 외워서 끝내는 네트워크 핵심 이론 - 응용
          • 네트워크 장치의 구조
            • 세 가지 네트워크 장치 구조
            • Inline 구조
            • Out of path 구조와 DPI 그리고 망중립
            • Proxy(클라이언트 입장) - 우회
            • Proxy(클라이언트 입장) - 보호와 감시
            • Reverse Proxy(서버 입장)
          • 인터넷 공유기의 작동 원리
            • 공유기 개요
            • Symmetric NAT
            • Full Cone 방식
            • Restricted Cone, Port Restricted Cone
            • 포트 포워딩
            • UPnP 와 NAT
          • 부하분산 시스템 작동 원리
            • L4 부하분산 무정지 시스템
            • 대규모 부하분산을 위한 GSLB
          • VPN과 네트워크 보안 솔루션
            • PN 과 VPN
            • IPSec VPN 과 터널링 개념
            • VPN 과 재택근무
        • 외워서 끝내는 SSL 과 최소한의 암호기술
          • 기초이론
            • Checksum (검사합)
            • Hash
          • 암호기술에 대한 이해
            • 대칭키
            • 비대칭키
          • PKI 시스템과 인터넷
            • 인터넷을 위한 비대칭키 체계
            • 공개키 신뢰를 위한 검증체계
            • 웹서비스와 공인인증서
      • 책
        • 그림으로 배우는 네트워크 원리
          • 1. 네트워크 기본
          • 2. 네트워크를 만드는 것
          • 3. 네트워크의 공통 언어 TCP/IP
    • SECURITY
      • 그 외
        • Basic Auth
        • HMAC 기반 인증
    • 그 외
      • 동기/비동기 & 블로킹/논블록킹
  • DB
    • 그 외
      • 인덱스(Index)
      • 트랜잭션(TRANSACTION)
      • 실무에서 외래키를 사용하지 않는 이유
      • ORM vs SQL Mapper
      • 문자열 vs DATE
      • EXPLAIN 명령어
    • 강의
      • Real MySQL 시즌 1
        • Part.1
          • 1강. CHAR vs VARCHAR
          • 2강. VARCHAR vs TEXT
          • 3강. COUNT(*) & COUNT(DISTINCT) 튜닝
          • 4강. 페이징 쿼리 작성
          • 5강. Stored Function
      • 토크온 41차. JPA 프로그래밍 기본 다지기
        • 1. JPA 소개
        • 2. JPA 기초와 매핑
        • 3. 필드와 컬럼 매핑
        • 4. 연관관계 매핑
        • 5. 양방향 매핑
        • 6. JPA 내부구조
        • 7. JPA 객체지향쿼리
        • 8. Spring Data JPA 와 QueryDSL 이해
    • 책
  • Software Development Methodology
    • TDD
      • 강의
        • Spring Boot TDD - 입문부터 실전까지 정확하게
          • 세션2. TDD 소개
          • 세션5. API 설계
          • 세션6. TDD 주기 첫 번째 경험
          • 세션7. TDD 주기 반복
      • 그 외
        • 단위 테스트(Unit Test) 작성의 필요성
        • JUnit5
          • A Guide to JUnit 5
          • Guide to JUnit 5 Parameterized Tests
          • AssertJ Exception Assertions
          • Testing in Spring Boot
          • Junit 과 Mockito 기반의 Spring 단위 테스트 코드 작성법
        • Code Coverage
          • Code Coverage?
    • DDD
      • 책
        • 도메인 주도 설계(Domain-Driven Design)
          • 04 - 도메인의 격리
          • 05 - 소프트웨어에서 표현되는 모델
          • 06 - 도메인 객체의 생명주기
          • 07 - 언어의 사용(확장 예제) (1)
          • 07 - 언어의 사용(확장 예제) (2)
        • 도메인 주도 개발 시작하기
          • 1. 도메인 모델 시작하기
          • 2. 아키텍처 개요
          • 3. 애그리거트
          • 4. 리포지터리와 모델 구현
            • DAO vs Repository
      • 강의
        • DDD 세레나데(NEXTSTEP)
          • 1주차
            • 도메인 주도 설계 등장 배경
            • 레거시 코드
            • 유연한 설계 - ASSERTION
          • 2주차
            • 전략적 설계 - UBIQUITOUS LANGUAGE
            • 전략적 설계 - BOUNDED CONTEXT
          • 3주차
            • 전술적 설계 - VALUE OBJECT 와 ENTITY
            • 전술적 설계 - AGGREGATE 와 REPOSITORY
            • 전술적 설계 - SERVICE
    • REFACTORING
      • 일급 컬렉션(First Class Collection) 소개와 사용해야하는 이유
  • ARCHITECTURE
    • Event Driven Architecture
  • 멘토링
    • F-Lab
      • 10회차(2024.12.29)
Powered by GitBook
On this page
  • 19.2 특수 연산자
  • 19.2.1 첨자 연산자 []
  • 19.2.2 함수 호출 ()
  • 19.2.3 역참조 →
  • 19.2.4 증가와 감소 ++ --
  • 19.2.5 할당 및 할당 해제 new delete
  • 19.2.6 사용자 정의 리터럴
  • 19.3 문자열 클래스
  • 19.3.1 필수 연산
  • 19.3.2 문자에 대한 접근
  • 19.3.3 표현
  • 19.3.4 멤버 함수
  • 19.3.5 보조 함수
  • 19.4 프렌드
  1. Language
  2. C++
  3. 책
  4. The C++ Programming Lanuaage
  5. 3부 : 추상화 메커니즘

19. 특수 연산자

19.2 특수 연산자

19.2.1 첨자 연산자 []

  • operator[] 함수는 클래스 개체에 대한 의미를 부여하는데 사용된다.

  • operator[] 함수의 두 번째 인자는 어떤 타입이든 가능하다. → 따라서 vector, 연관 배열 같은 것들도 정의할 수 있다.

  • operator[] () 는 비 static 멤버 함수여야 한다.

struct Assoc {
	vector<pair<string, int>> vec;   // {name, value} 쌍의 배열 
	
	const int& operator[] (const string&) const;
	int& operator[] (const string&);
}

// ... 

// s 를 찾는다. 그 값이 발견되면 참조자를 반환한다. 
// 그렇지 않을 경우 새로운 쌍 {s, 0} 을 만들고, 그 값에 대한 참조자를 반환한다. 
int& Assoc::operator[](const string& s) 
{
	for(auto : vec)
		if(s==x.first) return x.second; 
	vec.push_back({s,0});
	return vec.back().second;
}

int main() 
{
	Assoc values; 
	string buf; 
	while(cin>>buf) ++values[buf];
	for(auto x : values.vec) 
		cout << '{' << x.first << ',' << x.second << "}\\n";
}

19.2.2 함수 호출 ()

  • 함수 호출 표기 () 는 다른 연산자와 동일하게 오버로딩 될 수 있고, 아래와 같이 사용될 수 있다.

  • () 연산자의 가장 중요한 용도는 어떤 측면에서 함수처럼 동작하는 개체를 위한 통상적인 함수 호출 문법을 제공하는 것이다.

struct Action {
	int operator() (int); 
	pair<int, int> operator()(int, int);
	double operator() (double); 
	// ...
};

void f(Action act)
{
	int x = act(2); 
	int x = act(3,4); 
	int x = act(2.3); 
};
  • 이러한 개체를 이용하면 매개변수를 받아들이는 연산을 수행할 수 있다.

  • 많은 경우 함수 개체가 연산을 수행하는 데 필요한 데이터를 갖고 있을 수 있느냐가 중요하다.

  • 예를 들어 아래와 같이 저장된 값을 인자로 추가해주는 opearotr() () 를 가진 함수를 정의할 수 있다.

class Add {
	complex val;
public : 
	Add(complex c) : val{c} {}                       // 값을 저장한다. 
	Add(double r, double i) : val{{r,i}} {} 
	
	void operator() (complex& c) const {c += val;}   // 값을 인자에 추가한다. 
};
  • 아래 코드는 complex{2,3} 을 vec의 모든 원소에 추가하고 , complex{z} 를 lst 의 모든 원소에 추가 할 것이다.

    • for_each 함수의 3번째 인자는 opearotr() () 를 오버로딩 한 개체여야만 한다.

void h(vector<complex>& vec, list<complex>& lst, complex z) 
{
	for_each(vec.begin(), vec.end(), Add{2,3}); 
	for_each(lst.begin(), lst.end(), Add{z});
}

// ... 

template<typename Iter, typename Fct>
Fct for_eack(Iter b, Iter e, Fct f)
{
	while (b != e) f(*b++);
	return f;
}
  • 람다 표현식은 기본적으로 함수 개체를 정의하기 위한 문법이기에, 아래와 같이 작성할 수도 있다.

    • 이 경우 람다 표현식은 함수 개체 Add 와 똑같은 것을 생성한다.

    • operator() () 는 비 static 멤버 함수여야 한다.

void h2(vector<complex>& vec, list<complex>& lst, complex z)
{
	for_each(vec.begin(), vec.end(), [] (complex& a) {a+={2,3}; }); 
	for_each(lst.begin(), lst.end(), [] (complex& a) {a+=z; }); 
}

19.2.3 역참조 →

  • 역참조 연산자는 클래스 개체는 포인터가 쓰이는 방식과 매우 유사한 방식으로 클래스 X 의 멤버 변수에 접근하는데 사용될 수 있다.

  • 예를 들면 다음과 같다.

    • 개체 p 를 포인터 p.operator→() 로 변환하는 것은 가리켜지는 멤버 m 에 의존하지 않는다.

class Ptr {
	// ...
	X* operator->(); 
};

// ...

void f(Ptr p) 
{
	p->m = 7;   // (p.operator->()) -> m = 7
}
  • 하지만, 새로운 문법이 도입되지 않은 관계로, → 뒤에 여전히 멤버 이름이 필요하다.

void g(Ptr p)
{
	X* q1 = p ->              // 문법 오류 
	X* q2 = p.operator->();   // ok
}
  • → 오버로딩은 주로 ‘스마트 포인터’ 를 만드는 데 유용하다.

  • 이후 내용은 강의 후 ..

19.2.4 증가와 감소 ++ --

  • ‘스마트 포인터’ 가 등장하자 사람들은 증가 연산자 ++ 과 감소 연산자 -- 를 자주 사용하게 됐다. → 이 연산자들은 기본 제공 타입에 대해 쓰이는 용도를 흉내 내기 위해서다. → 이는 통상적인 포인터 타입을 ‘스마트 포인터’ 로 대체하는 것이 목적인 경우에 필요하다.

  • 스마트 포인터는 런타임 오류 체크가 추가된다는 점만 제외하면 통상적인 포인터와 동일한 의미 구조를 갖는다.

  • 다음 코드는 p 가 범위를 벗어난다는 문제가 있다.

void f1(X a) 
{
	X v[200];
	X* p = &v[0];
	p--; 
	*p = a;       // 문제 발생 : p 가 범위를 벗어난다! 
	++p; 
	*p = a;       // ok 
}
  • 개선된 아래 코드를 살펴보자

    • 여기서는 *X 를 Ptr<X> 클래스의 개체로 대체하려고 하는데, 이 개체는 실제로 X 를 가리키는 경우에만 역참조 될 수 있다.

    • 또한 p 가 배열 내에서 가리키고 증가와 감소 연산의 결과로 해당 배열에 포함된 개체가 나올 떄만 p 가 증가되고 감소될 수 있게 보장하려고 한다.

void f2(Ptr<X> a) 
{
	X v[200];
	Ptr<X> p(&v[0], v);
	p--;
	*p = a;   // runtime error : p 는 범위를 벗어남
	++p;
	*p = a    // ok
}
  • 증가, 감소 연산자는 전위형 및 후위형 연산자로 모두 사용 가능하다는 점에서 C++ 연산자 중에서도 특이하다.

  • 따라서 Ptr<T> 에 대해서 전위형 및 후위형의 증가, 감소 연산자를 정의해야 한다.

    • 후위 연산자에 int 인자는 해당 함수가 ++ 의 후위형 적용에 대해 호출된다는 점을 나타내기 위해 사용된다. → 실제 int 는 사용되지 않는다.

template<typename T> 
class Ptr {
	T* ptr; 
	T* array;
	int sz;
public : 
	template<int N> 
	Ptr(T* p, T(&a)[N];      // 배열 a 에 연결된다. sz==N, 초기 값 p
	Ptr(T* p, T* a, int s)   // s 크기를 갖는 배열 a 와 연결된다. 초기 값 p
	Ptr(T* p)                // 단일 개체와 연결된다. sz==0, 초기 값 p
	
	Ptr& operator++();      // 전위형
	Ptr& operator--();      // 전위형 
	Ptr operator++(int);    // 후위형
	Ptr operator--(int);    // 후위형 
	
	T& operator*();         // 전위형
};

19.2.5 할당 및 할당 해제 new delete

  • 일반적으로 new delete 연산자를 오버로딩 하는 것은 권장되지 않는다.. → 복잡도가 높아진다..

19.2.6 사용자 정의 리터럴

  • C++ 는 다양한 기본 제공 타입에 대한 리터럴을 제공한다.

123   // int
1.2   // double 
1.2F  // float
'a'   // char
1ULL  // unsigned long long
0xD0  // 16진수 unsigned
"aa"  // C 스타일 문자열
  • 추가적으로 우리는 사용자 정의 타입에 대한 리터럴과 기본 제공 타입에 대한 리터럴의 새로운 형식을 정의할 수 있다.

"Hi!"s                // 문자열, "0 으로 종료되는 char 의 배열" 이 아니다. 
1.2i                  // 허수 
101010111000101b      // 이진수 
123s                  // 초 
123.56km              // 마일이 아니다(단위)! 
1234567891234567890x  // 확장된 정밀도 
  • 정의하는 방법은 다음과 같다. → 리터럴 연산자의 이름은 operaotr”” 에 접미사가 뒤따르는 것이다.

    • 아래 두 개 연산자는 각각 접미사 i, s 를 정의하고,

    • constexpr 을 통해 컴파일 타임 평가를 이용한다.

constexpr complex<double> operator"" i(long double d)   // 허수 리터럴 
{
	return {0, d};   // complex 는 리터럴 타입이다. 
}

std::string operator"" s(const char* p, size_t n)   // std::string 리터럴 
{
	return string{p,n};   // 힙 영역에 할당을 요구한다. 
}

// ...

template<typename T> void f(const T&);
void g() 
{
	f("Hello");      // 포인터를 char* 에 전달한다. 
	f("Hello"s);     // (5개 문자) 문자열 개체를 전달한다. 
	f("Hello\\n"s);   // (6개 문자) 문자열 개체를 전달한다. 
	
	auto z = 2+1i;   // complex{2,1} 
}
  • 기본적인 계획은 리터럴일 수 있는 것을 구문 분석한 후에 컴파일러가 항상 접미사를 체크하는 것이다.

  • 접미사를 달아 사용자 정의 리터럴을 만들 수 있는 리터럴에는 네 종류가 있다.

    • 정수 리터럴

    • 부동소수점 리터럴

    • 문자열 리터럴

    • 문자 리터럴

19.3 문자열 클래스

  • 표준 라이브러리 std::string 에 비해 간소화 된 String 클래스를 구현해보자

19.3.1 필수 연산

  • String 클래스는 통상적인 생성자, 소멸자, 대입 연산의 집합을 제공한다.

class String {
public : 
	String();
	String(const char*p);
	String(const String&);
	String& operator=(const String&);
	String(String&& x);
	String& operator=(String&& x);
	~String() {if(short_max<sz) delete[] ptr;} 
	// ...
};

19.3.2 문자에 대한 접근

  • 문자에 대한 접근 연산자의 설계는 어려운 주제이다. → 이상적으로 접근은 관용적 표기법 []을 이용해야 하고, 최대한 효율적이고, 범위를 체크해야 하기 때문이다. → 하지만 이런 속성을 전부 수용할 수 있는 방법은 존재하지 않는다…

  • 여기서는 표준 라이브러리를 따라서 관용적인 [] 연산자와 범위 체크 at() 연산을 제공하고자 한다.

class String {
public : 
	// ... 
	char& operator[](int n) {return ptr[n];}     // 체크되지 않은 원소 접근 
	char operator[](int n) const {return ptr[n];} 
	
	char& at(int n) {check(n); return ptr[n];}   // 범위 체크 후 원소 접근 
	char at(int n) const {check(n); return ptr[n];}
	
	String& operator+=(char c);                  // 끝에 c 를 추가한다. 
	
	char* c_str() {return ptr};                  // C 스타일 문자열 접근 
	const char* c_str() const {return ptr;}      
	
	int size() const {return sz;}                // 원소의 개수 
	int capacity() const                         // 원소 더하기에 이용 가능한 공간
	{ return (sz<=short_max) ? short_max : sz+space;}
	// ... 
};
  • [] 를 통상적인 용도로 사용하자는 것이 기본 구상이다.

    • 여기서는 at() 의 사용이 불필요한데, s 에 대해 0에서 s.size()-1 까지만 접근하기 때문이다.

int hash(const String& s) 
{
	if(s.size() == 0) return 0;
	int h{s[0]};   // s 에 대한 체크되지 않은 접근 
	
	for(int i{1}; i<s.size(); ++i)
		h ^= s[i] >> 1   // s 에 대한 체크되지 않은 접근 
	return h;
}
  • 실수의 가능성이 보이는 곳에서는 at() 을 사용할 수 있다.

    • 실수 가능성이 있는 곳에서도 [] 을 사용할 수 있기 때문에, std::string 표준 라이브러리의 일부 [] 구현에서는 범위를 체크한다.

    • 하지만, 범위 체크에는 오버헤드가 발생한다는 점을 생각하고 상황에 따라서 구현이 필요하다.

void print_in_order(const String& s, const vector<int>& index)
{
	for(auto x : index)
		cout << s.at(x) << '\\n';
}

19.3.3 표현

  • String 에 대한 표현은 세 가지 목표를 충족하게 선택됐다.

    • C 스타일 문자열을 String 으로 변환하기 쉬고, C 스타일 문자열로서 String 문자에 쉽게 접근할 수 있게

    • 힙 공간의 사용을 최소화하게

    • String 끝에 문자를 효율적으로 추가할 수 있게

  • 결과는 복잡도가 올라갔지만, 효율적으로 동작한다.

  • 아래 코드는 두 개의 문자열 표현을 통해서 **짧은 문자열 최적화 기법(short string optimization)**을 구현한다.

    • sz≤short_max 라면 문자들이 String 개체 내 ch 란 이름의 배열로 저장된다.

    • !(sz≤short_max) 라면 문자들이 힙 영역에 저장되고 확장을 위한 추가 공간을 할당할 수 있다.

  • 두 경우 모두 ptr 은 원소를 가리킨다.

    • 접근 함수는 어떤 표현이 쓰이고 있는지 검사할 필요가 없다.

    • 그저 ptr 만 사용되면 되는 것이다.

  • sz≤short_max 인 경우에만 배열 ch 를 사용하고, !(sz≤short_max) 일 때는 정수 space 를 사용하기 때문에 ch 와 space 두 개 모두에 대한 공간을 할당하는 것은 낭비가 될 것이다. → 이런 낭비를 피하기 위해서 union 을 사용한다.

class String {

public:
// ...
private:
	static const int short_max = 15;
	int sz;                  // 문자의 개수 
	char∗ ptr;
	union {
		int space;             // 사용되지 않는 할당된 공간 
		char ch[shor t_max+1]; // 종료에 쓰이는 0을 위해 공간을 남겨둔다 
	};
	void check(int n) const  // 범위 체크 
	{
		if (n<0 || sz<=n)
		throw std::out_of_rang e("String::at()");
	}
	// 부속 멤버 함수
	void copy_from(const String& x);
	void move_from(String& x);
};

19.3.4 멤버 함수

  • 기본 생성자는 String 이 비어 있게 정의한다.

String::String() 
	: sz{0}, ptr{ch}
{
	ch[0] = 0;
}
  • copy_from(), move_from() 이 있으면 생성자, 이동, 대입의 구현이 상당히 간단해진다.

    • 아래 코드에서는 공간 체크, 새로운 공간 할당, 등 .. 상당히 많은 일들이 벌어지고 있다.

    • 이런 코드의 복잡성을 생각하고 싶지 않다면 std::string 을 사용하자. → 라이브러리를 사용하는 이유가 바로 이것이다!

String::String(const char* p) 
	: sz{strlen(p)},
	ptr{(sz<=short_max) ? ch : new char[sz+1]},   // 길이가 길다면 힙 영역에 할당한다. 
	space{0}
{
	strcpy(ptr, p);
}

String::String(const String& x) 
{
   copy_from(x);
}

String::String(String&& x) 
{
	move_from(x);
}

String& String::operator=(const String& x) 
{
	if(this==&x) retrun *this;
	char* p = (short_max<sz) ? ptr : 0;
	copy_from(x); 
	delete[] p;
	return *this; 
}

String& String::operator(String&& x) 
{
	if(this==&x) return *this;
	if(short_max<sz) delete[] ptr; 
	move_from(x);
	return *this;
}

String& String::operator+=(char c) 
{
	if(sz==short_max) {
		int n = sz+sz+2; 
		ptr = expand(ptr, n); 
		space = n-sz-2; 
	}
	else if(short_max<sz) {
		if(space==0)
			int n = sz+sz+2; 
			char* p = expand(ptr, n);
			delete[] ptr;
			ptr = p;
			space = n-sz-2; 
		else
		--space;
	}
	ptr[sz] = c;
	ptr[++sz] = 0;
	
	return *this; 
}

19.3.5 보조 함수

  • 그 외 입출력, 연산자 오버로딩 등.. 수 많은 함수 ..

19.4 프렌드

  • 통상적인 멤버 함수 선언은 논리적으로 구분되는 3가지 속성을 지정한다.

    • 함수는 클래스 선언의 비공개 부분을 접근할 수 있어야 한다.

    • 함수는 클래스의 유효 범위 내에 있어야 한다.

    • 함수는 개체에 대해 호출될 수 있어야 한다. (this 포인터를 가진다)

  • 즉 friend 로 선언된 함수는 멤버 함수와 똑같이 클래스의 구현에 접근이 혀용되지만, 그렇지 않을 경우에 해당 클래스와 무관하다.

  • friend 선언은 클래스 선언의 private 부분이나 public 부분 중 어느 쪽에도 놓일 수 있다. → 어느 곳에 선언되는 것은 중요하지 않다.

  • 다음과 같이 함수에 friend 선언을 할수도 있고, 클래스에 friend 선언을 할수도 있다.

class List_iterator {
	// ...
	int* next();
}; 

class List {
	friend int* List_iterator::next(); // 함수에 friend 선언
	// ...
};

class List {
	friend class List_interator;       // 클래스에 friend 선언
	// ...
};
Previous18. 연산자 오버로딩Next20. 파생클래스

Last updated 11 months ago