Pyhton을 하다가 c/cpp로 넘어오면 가장 불편한게 아마 연산자 아닐까 싶다.
파이썬에서는 사실상 못쓰는 연산자는 ++ -- 말고는 없다.
Str끼리 ==으로 비교도 가능하고, 논리연산자도 and or같은 단순한 단어로 바꿔쓸 수 있고, 심지어는 0 <= x < n 같이 수식비슷하게 쓸 수 도 있다.
C에서는 연산자를 만들어 쓴다는게 불가능하겠지만, CPP에서는 연산자 오버로딩을 통해 나만의 연산자를 만들어서 쓸 수 있다.
(리턴 타입) operator(연산자) (연산자가 받는 인자)
위의 형식을 따르면
bool operator==(MyString& str);
이 되고, 이제 str1 == str2 라고 쓴다면 내부적으로 str1.operator==(str2)로 작동하게 된다.
복소수는 허수와 실수의 합으로 나타나게 된다. 따라서 z = a + bi 구조를 가지고 있다.
이를 단순하게 구현하면 다음과 같다.
이는 오버로딩을 모른다고 가정했을때의 방식이다.
class Complex {
private:
double real, img;
public:
Complex(double real, double img) : real(real), img(img) {}3
Complex plus(const Complex& c);
Complex minus(const Complex& c);
Complex times(const Complex& c);
Complex divide(const Complex& c);
};
이러한 경우 기존의 int형이면 a + b / c + d; 같이 쓸 수 있었겠지만, a.plus(b.divide(c)).plus(d); 처럼 불편하고 가독성 떨어지게 써야한다.
이를 이제 오버로딩을 이용하여 작성해보면
#include <iostream>
class Complex {
private:
double real, img;
public:
Complex(double real, double img) : real(real), img(img) {}
Complex(const Complex& c) { real = c.real, img = c.img; }
Complex operator+(const Complex& c) const;
Complex operator-(const Complex& c) const;
Complex operator*(const Complex& c) const;
Complex operator/(const Complex& c) const;
void println() { std::cout << real << ", " << img << std::endl; }
};
Complex Complex::operator+(const Complex& c) const {
Complex temp(real + c.real, img + c.img);
return temp;
}
Complex Complex::operator-(const Complex& c) const {
Complex temp(real - c.real, img - c.img);
return temp;
}
Complex Complex::operator*(const Complex& c) const {
Complex temp(real * c.real - img * c.img, real * c.img + img * c.real);
return temp;
}
Complex Complex::operator/(const Complex& c) const {
Complex temp(
(real * c.real + img * c.img) / (c.real * c.real + c.img * c.img),
(img * c.real - real * c.img) / (c.real * c.real + c.img * c.img)
);
return temp;
}
int main() {
Complex a(1.0, 2.0);
Complex b(3.0, -2.0);
Complex c = a * b;
c.println();
}
다음과 같은 결과를 가진다.
이 클래스에서 주목해야할점은
operator는 반환값으로 complex 객체를 반환하게 되는데 이는 덮어쓰기 오류를 방지하기 위함이다.
만약
Complex Complex::operator+(const Complex& c) const {
real += c.real;
img += c.img;
return this;
}
같이 작성한다고 하자. 이때 b + c + b 를 실행하면
(b.plus(c)).plus(b)의 구조로 작동하게 되는데
이떄 this값을 반환하게 되면, b는 b + c의 값을 가지게 되고, 이는 곳 (b + c) + (b + c)의 구조가 된다.
대입연산자 역시 작성해줘야한다. 대입연산자는 레퍼런스를 인자로 가진다.
#include <iostream>
class Complex {
public:
Complex& operator=(const Complex& c);
};
Complex& Complex::operator=(const Complex& c) {
real = c.real;
img = c.img;
return *this;
}
int main() {
Complex a(1.0, 2.0);
Complex b(3.0, -2.0);
Complex c(0.0, 0.0);
c = a * b + a / b + a + b;
c.println();
}
다만 이러한 대입오퍼레이터를 안만들어도 기본으로 가지고있는 defalut 생성자로인해 대입은 잘 작동한다. 다만 이는 얕은 복사에 해당하기에 깊은 복사를 위해서라면 따로 설정해야한다.
어떠한 클래스, 혹은 함수가 친구임을 나타내는 관계이다. 깐부끼리는 니꺼 내꺼가 없다고 하지 않는가? 그래서 이러한 선언을 받은 경우, 다른 쪽에서 내 private에 있는 모든것들에 접근이 가능해진다.
class A {
private:
void private_func() {}
int private_num;
friend class B;
friend void func();
};
class B {
public:
void b() {
A a;
a.private_func();
a.private_num = 2;
}
};
void func() {
A a;
a.private_func();
a.private_num = 2;
}
int main() {}
class A 내부에서 class B의 친구임이 선언되었고, 동시에 void func()와도 친구임이 선언되었다. 이에 B안에서는 A에 자유롭게 접근할 수있다. 또, func() 역시 자유롭게 접근이 가능하다.
하지만 반대로 A는 B의 친구선언을 받지 못했기때문에, 자유롭게 접근할 수 없다.
이 밖에도 이항연산자 오버로딩이나, 증감자 오버로딩, cout 오버로딩등 다양한 오버로딩이 존재한다
이글 을 보고 더 공부해야겠다