cpp module 02

이호용·2021년 8월 20일
0

cpp

목록 보기
3/16

흐흐 또시작이다,

Ad-hoc polymorphism, operators overload and
orthodox canonical classes

인스턴스란?

인스턴스는 객체가 메모리에 구현된 실체라고 한다.

class test{
}
이렇게 클래스를 생성하면 test가 객체다.

test a;
test b;
이렇게 a나 b같은 객체들 중에 a라는 인스턴스가 있다. 인스턴스랑 객체랑은 다른 용어지만 아직 정확하게 정의를 못하겠다. 대충 어렴풋이.. 정리만 했다. a와 b는 메모리상 올라갔으므로 인스턴스인데 이를 포함하는게 객체라고 한다. 무슨말인지 모르겠다고? 정상이다. 객체랑 인스턴스는 비슷하면서도 조금 다른가 보다. 잘 모르겠다!!~~(이해하는것도 좋지만 일단 넘어가는걸 추천한다.)

멤버함수 뒤에 const

가끔 맴버함수를 작성하다 보면 함수 뒤에 const가 붙는경우가 있다.

class Point {

private:

int xx, yy;


Point();

Point(int x, int y);

inline int GetX() const {

return x_;

}

이런 경우 get함수에서 xx, yy를 바꿀수 없다. get 함수내에서

std::cout << this->xx << std:endl;

인데 여기서 this가 const형태라고 생각하면 된다. 고로 해당 클래스 내의 변수는 아무것도 바꿀수 없다.

Ad-hoc polymorphism (임시 다형성)

다형성 ( Polymorphism ) 이란?
다형성은 원래 생물학에서 유래한 단어인데, 동일한 종(Species)에 속하는 생물이라 할지라도 다양한 변이를 가지는 현상을 나타낸다

프로그램 언어의 다형성은 각 요소들(상수, 변수, 식, 오브젝트, 함수, 메소드 등)이 다향한 자료형에 속하는 것이 허가되는 성질을 가리킨다. ex) 상속

다형성에 대한 자료; Modern CPP

  • 서브타입 다형성: 런타임 다형성(Runtime Polymorphism
  • 매개 변수 다형성: 컴파일 타임 다형성(Compile-Time Polymorphism)
  • 임시 다형성은: 오버로딩(Overloading)
  • 강제 다형성: (암시적 또는 명시적) *캐스팅(Casting)

출처 : 다형성에대한 글들이있는데 들어가서 가볍게 읽어보시면 좋아요

operators overload (연산자 오버로드)

#include <iostream>
using namespace std;

struct Complex {
   Complex( double r, double i ) : re(r), im(i) {}
   Complex operator+( Complex &other );
   void Display( ) {   cout << re << ", " << im << endl; }
private:
   double re, im;
};

Complex Complex::operator+( Complex &other ) {
   return Complex( re + other.re, im + other.im );
}

int main() {
   Complex a = Complex( 1.2, 3.4 );
   Complex b = Complex( 5.6, 7.8 );
   Complex c = Complex( 0.0, 0.0 );

   c = a + b;
   c.Display();
}

출처

operator+라고 멤버함수를 선언해 두면, 해댕인스턴스를
complex a 라는게 있을때 a + b이런식으로 연산으로 가능하게 만든다.

가독성이 좋게하는 장점? 실제로는 a + b가 동작했을때
a.operator+(b)와 같다.

Orthodox canonical classes

canonical이란, "규정대로"하는 프로그래밍을 의미하며,
말그대로 클래스를 사용할때 Orthodox canonical 규칙들을 따르는것이다.

아래 4가지 규칙을 사용하지 않더라고 규정대로 클래스를 작성해야한다.

  1. 디폴트 생성자(Default constructor)
  2. 복사 생성자(Copy constructor)
  3. 할당 연산자 오버로딩(Copy assignment operator)
  4. 소멸자(Destructor)

아래 orthodox는 그냥 이런게 있다. 하고 넘어가자.
대현님 블로그 참고

1. 디폴트 생성자

지금까지 사용한 생성자를 말한다.

디폴트 생성자가 생성되는 조건은 어떤한 생성자도 정의되 않았을 때 생성된다.(메게변수를 받는 생성자도 생성될수 있을음)

class a{
a(){}
a(int i) {
std::cout << "오버로드" << i;
}
};

2. 복사 생성자

C++에서 복사 생성자는 다른 객체의 값을 복사해서 초기화하는데 사용하는 생성자 입니다.

복사생성자의 예시(1)

int main(){
	test s1("안녕") // 생성자!!
    test s2(s1); //복사 생성자
    
    cout << s1.get_print() << endl;
    cout << s2.get_print() << endl;
}

test s2(s1)

s1의 경우 정상적으로 test::test (string str) 의 생성자가 실행이 된다. 하지만 s2를 선언할땐 매게변수로 s1이라는 인스턴스가 들어가있고, 해당 선언은 기본 생성자가 아닌 복사 생성자가 실행된다.

class test{
private:
	char *strData;
	len = 0;
public:
	test(test &s1) {//복사 생성자
    	s2::strData = s1.strData; // 얕은 복사(strData일 경우 포인터이므로 주소가 복사되버림.)
        s2::len = s1.len; ///깊은 복사
    }
}

얕은 복사, 깊은복사 예시

int main (){
	int *a = new int(5);
    int *b = new int(3);
    
    a = b; //얕은 복사(참조만 복사)
    *a = *b; // 깊은 복사(값을 복사)
}

할당연산자 오버로드

private:
	int len;
    char *strData;
public:
int main(){
	test s1("안녕");
   	test s3;
    
    s3 = s1; //선언후 대입, 이 경우도 c++ 에서 기본적으로 얕은 복사가 일어난다. 연산자오버로드 만드는거에 따라 다르겠지만..
}

얕은 복사는 참조나 주소값을 넣어주는 경우.

class test{
	test &operator=(const test &s1){ //&operator라고 앞에 &를 붙인이유는..객체 복사를 안하고 그냥 지금 인스턴스를 복사안하고 넘길려고
    	strData = s1.strdata;
        len = s1.len;
        return *this;
    }
}

깊은 복사는 값만 넣어주는 경우다.

Orthodox

Orthodox C++란?

: 모던 C++ 에서 불필요한 것들을 다 빼고, C++ 에서 최소한의 필수적인 기능들만 사용하고자 하는 것

Orthodox C++ 을 쓰는데?

: Orhodox C++ 을 쓴 코드는 이해하기 더 쉽고, 코드 자체가 심플하고, 옛날 컴파일러에서도 빌드가 잘 됨.

뿐만 아니라 Orthodox C++ 으로 쓴 프로젝트는 대개 다른 프로젝트에서 적용하고 있는 가이드라인을 위반하지 않는 경우가 많기 때문에,

다른 프로젝트에 적용하기도 쉽다고 함.

그럼 뭘 써야하는데?

: C 스타일의 C++ 가 대개 기본 스타일임.
: boost 구현체의 이따구 짓을 따라하지 말고, 그냥 심플심플하게 짜는 것을 추천.

(https://archive.md/2014.04.28-125041/https://www.boost.org/doc/libs/1_55_0/libs/geometry/doc/html/geometry/design.html)

: 익셉션 금지
: RTTI 금지
: C++ 런타임 랩퍼 (cstdio, cmath 등) 을 사용하지 말고, C 라이브러리를 사용할 것.
: 스트림 금지
: 프로젝트 자체에서 메모리 관리를 할거라면 메모리 할당을 하는 STL 라이브러리들 역시 사용금지
: 학술적인 자 | 위 목적으로 런타임 블롯 아님 컴파일 빌드 타임 블롯되는 메타 프로그래밍 떡칠하지 말 것.
: C++11 이나 C++14 같은 신규 사양들을 사용하고 싶으면, 해당 버전의 다음 버전의 C++ 가 채택될 때 사용할 것.

<<, >> 입출력 연산자 전역변수

>> << 입출력 연산자 오버로딩은 멤버 함수로는 구현할 수 없고 전역 함수로만 구현할 수 있다.
여기서 보자!

연산자 오버로딩이, ostream& 이런식으로 참조로 반환하는 이유는. 자기 자신에게 다시 자신의 주소를 반환해서 다음 연산을 이어나갈려고 하는거다.

case 1.

cout을 사용할 때, std::cout << "test" 이런식으로 출력하면, ostream클래스에 저장되어있는 operator<< 멤버할수를 읽어온다.

ostream& operator<<(const char *);
ostream& operator<<(char);
ostream& operator<<(short);

case 2.

cout 을 사용할 때, std::cout.operator<< ("test")를 출력하면,
ostream클래스에 저장되어있는 ostream::operator<<을 불러온다.

ostream& ostream::operator<<(const char *);
ostream& ostream::operator<<(char);
ostream& ostream::operator<<(short);

case 3.

만약 cout 에 없는 operator을 호출하면?
아래 코드에서 a는 인스턴스다.
std::cout << a 일때, ostream에 ostream& operator<<(const Fixed *);은 없고, operator<<(cout, A) 으로 해석되어 전역변수중에 해당 오퍼레이트가 있는지 확인하게 된다.

std::ostream &operator<<(std::ostream &out, Fixed const &value);
int main(void){
Fixed a; //Fixed는 클래스

std::cout << a;
}

여기서 많은 영감을...

0개의 댓글