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 함수 호출 ()
()함수 호출 표기
()는 다른 연산자와 동일하게 오버로딩 될 수 있고, 아래와 같이 사용될 수 있다.()연산자의 가장 중요한 용도는 어떤 측면에서 함수처럼 동작하는 개체를 위한 통상적인 함수 호출 문법을 제공하는 것이다.
이러한 개체를 이용하면 매개변수를 받아들이는 연산을 수행할 수 있다.
많은 경우 함수 개체가 연산을 수행하는 데 필요한 데이터를 갖고 있을 수 있느냐가 중요하다.
예를 들어 아래와 같이 저장된 값을 인자로 추가해주는
opearotr() ()를 가진 함수를 정의할 수 있다.
아래 코드는
complex{2,3}을vec의 모든 원소에 추가하고 ,complex{z}를lst의 모든 원소에 추가 할 것이다.for_each함수의 3번째 인자는opearotr() ()를 오버로딩 한 개체여야만 한다.
람다 표현식은 기본적으로 함수 개체를 정의하기 위한 문법이기에, 아래와 같이 작성할 수도 있다.
이 경우 람다 표현식은 함수 개체 Add 와 똑같은 것을 생성한다.
operator() ()는 비 static 멤버 함수여야 한다.
19.2.3 역참조 →
→역참조 연산자는 클래스 개체는 포인터가 쓰이는 방식과 매우 유사한 방식으로 클래스
X의 멤버 변수에 접근하는데 사용될 수 있다.예를 들면 다음과 같다.
개체 p 를 포인터
p.operator→()로 변환하는 것은 가리켜지는 멤버 m 에 의존하지 않는다.
하지만, 새로운 문법이 도입되지 않은 관계로,
→뒤에 여전히 멤버 이름이 필요하다.
→ 오버로딩은 주로 ‘스마트 포인터’ 를 만드는 데 유용하다.
이후 내용은 강의 후 ..
19.2.4 증가와 감소 ++ --
++ --‘스마트 포인터’ 가 등장하자 사람들은 증가 연산자
++과 감소 연산자--를 자주 사용하게 됐다. → 이 연산자들은 기본 제공 타입에 대해 쓰이는 용도를 흉내 내기 위해서다. → 이는 통상적인 포인터 타입을 ‘스마트 포인터’ 로 대체하는 것이 목적인 경우에 필요하다.스마트 포인터는 런타임 오류 체크가 추가된다는 점만 제외하면 통상적인 포인터와 동일한 의미 구조를 갖는다.
다음 코드는
p가 범위를 벗어난다는 문제가 있다.
개선된 아래 코드를 살펴보자
여기서는
*X를Ptr<X>클래스의 개체로 대체하려고 하는데, 이 개체는 실제로X를 가리키는 경우에만 역참조 될 수 있다.또한
p가 배열 내에서 가리키고 증가와 감소 연산의 결과로 해당 배열에 포함된 개체가 나올 떄만 p 가 증가되고 감소될 수 있게 보장하려고 한다.
증가, 감소 연산자는 전위형 및 후위형 연산자로 모두 사용 가능하다는 점에서 C++ 연산자 중에서도 특이하다.
따라서
Ptr<T>에 대해서 전위형 및 후위형의 증가, 감소 연산자를 정의해야 한다.후위 연산자에
int인자는 해당 함수가++의 후위형 적용에 대해 호출된다는 점을 나타내기 위해 사용된다. → 실제int는 사용되지 않는다.
19.2.5 할당 및 할당 해제 new delete
new delete일반적으로
newdelete연산자를 오버로딩 하는 것은 권장되지 않는다.. → 복잡도가 높아진다..
19.2.6 사용자 정의 리터럴
C++ 는 다양한 기본 제공 타입에 대한 리터럴을 제공한다.
추가적으로 우리는 사용자 정의 타입에 대한 리터럴과 기본 제공 타입에 대한 리터럴의 새로운 형식을 정의할 수 있다.
정의하는 방법은 다음과 같다. → 리터럴 연산자의 이름은 operaotr”” 에 접미사가 뒤따르는 것이다.
아래 두 개 연산자는 각각 접미사
i, s를 정의하고,constexpr을 통해 컴파일 타임 평가를 이용한다.
기본적인 계획은 리터럴일 수 있는 것을 구문 분석한 후에 컴파일러가 항상 접미사를 체크하는 것이다.
접미사를 달아 사용자 정의 리터럴을 만들 수 있는 리터럴에는 네 종류가 있다.
정수 리터럴
부동소수점 리터럴
문자열 리터럴
문자 리터럴
19.3 문자열 클래스
표준 라이브러리 std::string 에 비해 간소화 된 String 클래스를 구현해보자
19.3.1 필수 연산
String 클래스는 통상적인 생성자, 소멸자, 대입 연산의 집합을 제공한다.
19.3.2 문자에 대한 접근
문자에 대한 접근 연산자의 설계는 어려운 주제이다. → 이상적으로 접근은 관용적 표기법
[]을 이용해야 하고, 최대한 효율적이고, 범위를 체크해야 하기 때문이다. → 하지만 이런 속성을 전부 수용할 수 있는 방법은 존재하지 않는다…여기서는 표준 라이브러리를 따라서 관용적인
[]연산자와 범위 체크at()연산을 제공하고자 한다.
[]를 통상적인 용도로 사용하자는 것이 기본 구상이다.여기서는
at()의 사용이 불필요한데,s에 대해 0에서s.size()-1까지만 접근하기 때문이다.
실수의 가능성이 보이는 곳에서는
at()을 사용할 수 있다.실수 가능성이 있는 곳에서도
[]을 사용할 수 있기 때문에,std::string표준 라이브러리의 일부[]구현에서는 범위를 체크한다.하지만, 범위 체크에는 오버헤드가 발생한다는 점을 생각하고 상황에 따라서 구현이 필요하다.
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을 사용한다.
19.3.4 멤버 함수
기본 생성자는 String 이 비어 있게 정의한다.
copy_from(),move_from()이 있으면 생성자, 이동, 대입의 구현이 상당히 간단해진다.아래 코드에서는 공간 체크, 새로운 공간 할당, 등 .. 상당히 많은 일들이 벌어지고 있다.
이런 코드의 복잡성을 생각하고 싶지 않다면
std::string을 사용하자. → 라이브러리를 사용하는 이유가 바로 이것이다!
19.3.5 보조 함수
그 외 입출력, 연산자 오버로딩 등.. 수 많은 함수 ..
19.4 프렌드
통상적인 멤버 함수 선언은 논리적으로 구분되는 3가지 속성을 지정한다.
함수는 클래스 선언의 비공개 부분을 접근할 수 있어야 한다.
함수는 클래스의 유효 범위 내에 있어야 한다.
함수는 개체에 대해 호출될 수 있어야 한다. (
this포인터를 가진다)
즉
friend로 선언된 함수는 멤버 함수와 똑같이 클래스의 구현에 접근이 혀용되지만, 그렇지 않을 경우에 해당 클래스와 무관하다.friend선언은 클래스 선언의private부분이나public부분 중 어느 쪽에도 놓일 수 있다. → 어느 곳에 선언되는 것은 중요하지 않다.다음과 같이 함수에
friend선언을 할수도 있고, 클래스에friend선언을 할수도 있다.
Last updated