반응형

C++하면 가장 중요한 키워드가 하나 있다. 그건 바로 클래스(Class) 이다.

객체 지향 프로그래밍은 클래스에서 시작했다고 해도 과언이 아니며

오늘은 이 클래스란 것을 들여다볼 예정이다.

아시다시피 해당 내용은 다섯개 챕터로 나눠써도 부족한 만큼 크게크게 요약하여 작성하도록 하겠다.


1. 클래스(Class)?

클래스란 객체에서 불필요한 특성을 제거해 모델링해서 공통된 특성을 하나의 패키지안에 놓은 것이다.

맞다.. 이렇게 생각하면 어렵다. 쉽게 말해서 '구조체의 확장 버전'이라고 생각하면 편하다.

 

클래스는 구조체와 달리 각 타입별 변수의 집합 뿐만 아니라, 멤버 함수까지 포함되고

접근 제어 지정자를 선언하여 사용 가능하다.

 

여기서 클래스와 구조체에 차이점을 발견할 수 있는데 접근 제어 지정자를 따로 선언하지 않는다면

  • 구조체(struct)는 멤버변수가 public으로 선언되며,
  • 클래스(class)에선 멤버변수가 private으로 선언된다.

public과 private에 대해선 아래에서 설명하도록하겠다.

 

2. 클래스 선언

클래스 이용을 위해선 세가지 구역이 필요하다.

  • 클래스 선언 구역
  • 클래스 정의 구역
  • 클래스 사용 구역

클래스 사용을 위해선 클래스 선언부터 해주어야 한다.

 

우선 예제 코드를 보자.

class Friend {
//멤버 변수
private: //내부에서만 접근 가능(default라 생략 가능)
	int age;
	char *name;
//멤버 함수
public: //외부에서 접근 가능
	void printInfo(){}; 
	void printInfo2(); //내부 클래스 정의, 외부 클래스 정의 모두가능
};

void Friend::printInfo2() {
}

클래스는 다음과 같이 선언하게 된다. class <클래스명>으로 선언해준다.

이후 접근제어 지정자와 함께 멤버 변수나 멤버 함수를 사용해주면 된다.

 

멤버 함수는 위와 같이 내부 클래스에서 정의 가능하며 외부에서도 정의 가능하다.

여기에서 꼭 선언한 이후 세미콜론을 붙여주도록 한다.

 

잠깐, 접근제어 지정자에 대해 짚고 넘어가자.

  • public : 공개적 접근, 클래스 밖을 포함해 모든 곳에서 접근 가능하다.
  • protected : 클래스가 속한 클래스 내부나 해당 클래스로부터 파생한 클래스 내부에서만 접근 가능하다.
  • private (default) : 클래스가 속한 클래스의 내부만 접근 가능하며, 외부 접근 불가하다.

    *모든 멤버 변수는 꼭 필요한 경우가 아니면 protected나 private으로 외부 공개 안하는게 원칙이다.

    *일반적으로 멤버 변수는 private하며, 이에 안전하게 접근할 수 있는 멤버 함수는 public

 

 

3. 객체 생성

객체 생성을 위해선 다음과 같이 사용한다.

  • <클래스이름> <객체이름>;

 

멤버 사용을 위해선 다음과 같은 형태로 사용한다.

  • <객체이름>.<멤버변수>;
  • <객체이름>.<멤버함수()>;

 

당연히 초기화도 가능하며 예제로 살펴보자.

int main(void) {
	Friend kim;
	kim.age = 21; //멤버 변수 초기화, 해당 멤버 변수가 public이여야 직접 변경 가능
	kim.printInfo(); //멤버 함수 호출

	return 0;
}

 

Frient 클래스로 kim이란 객체를 생성하고, 안에 age를 초기화해주었다.

해당 부분처럼 age를 직접 초기화하기 위해선 클래스에서 int age가 public이어야 한다.

 

일반적으로 private에 선언하고 멤버함수로 setInfo()와 같은 함수를 만들어 변경해주는 것이 좋으며

본 예제에선 이해를 위해 위와 같이 작성하였다.

 

 

4. 생성자와 소멸자

생성자객체가 인스턴스화 될 때 컴파일러에 의해 호출되는 특수 메소드 함수이다.

디폴트 생성자, 매개변수 생성자, 복사 생성자가 존재하며 상황에 맞게 사용하게 된다.

특징은 다음과 같다.

  • 생성자는 클래스 이름과 반드시 같아야 함.
  • 생성자는 return을 가질 수 없음.

 

소멸자는 이와 반대로 객체 사용을 마쳤을 때 컴파일러에 의해 호출되는 특수 멤버 함수이다.

특징은 다음과 같다.

  • 객체가 유효 범위 벗어날 때 자동 호출
  • 클래스 이름 앞에 ~를 붙인다.

여기서 디폴트 생성자와 소멸자는 기본적으로 생성되며,

일반적으로 클래스 내부에서 동적할당 메모리를 할당, 해제할 때 사용된다.

 

예제를 보며 사용해보자.

#include <iostream>
using namespace std;

class Friend {
//멤버 변수
private:
	int age;
	const char* fname;
//멤버 함수
public:
	Friend(); //생성자
	~Friend(); //소멸자
	void printInfo(); //내부 클래스 정의, 외부 클래스 정의 모두가능
};
Friend::Friend() {
	cout << "생성자 호출" << endl;
	age = 24;
	fname = "KimJaeHo";
}
Friend::~Friend() {
	cout << "소멸자 호출" << endl;
}
void Friend::printInfo() {
	cout << "나이 : " << age << ", 이름 : " << fname << endl;
}

int main(void) {
	Friend kim;
	kim.printInfo(); //멤버 함수 호출

	return 0;
}

 

 

결과는 다음과 같다.


여기까지 클래스에 대한 공부를 마친다.

클래스에는 이외에도 오버로딩과 더불어 중요한 여러가지 개념들이 더 존재한다.

해당 포스팅에선 여기서 설명을 마치지만 함께 더 공부해봐야 할 것이다.

 

반응형

'Programming > Language' 카테고리의 다른 글

[Assembly] 파일 읽고 출력해보기  (0) 2020.06.06
[Assembly] 문자열 입력과 출력  (0) 2020.06.05
[Assembly] 1부터 10까지의 합 계산  (0) 2020.06.04
[C++] STL - multimap 공부  (0) 2020.05.24
[C++] STL - map 공부  (0) 2020.05.14

반응형

로버트 C.마틴의 '클린 코드'를 3장까지 읽고나서

이 책은 개발자들의 '필독서'라는 생각을 하게 되었다.

깃허브에서 잘 짜여진 소스코드와 내 소스코드를 비교해보며

어디서 차이가 발생하였는지 고민해본 경험이 있었다.

책을 읽고 답을 찾았다. 핵심은 '클린 코드'였다.

지금까지 공부한 지식을 개인적인 생각으로 녹여보고자 한다.


1. 클린코드란?

"유지보수가 쉬운 코드, 곧 읽기 좋은 코드"

 

 클린 코드란 '가독성이 높으며, 직접적인 코드'이다. 책에선 다방면으로 이야기 하는데 생각을 정리하자면 이렇다.

 

제3자가 코드를 읽거나 수정하기 불편하다면 그건 '클린 코드'가 아니다.

int AB(int a, int b){
    int c;
    c = a + b ;
    return c;
}


제3자가 코드를 읽거나 수정하기 쉬우면
그건 '클린 코드'다.

 

int getSum(int firstArg, int secondArg){
    int sum = firstArg + secondArg;
    return sum;
}

 

보다 싶이 클린 코드는 '길이'와 상관 없다.

제3자는 AB() 함수를 보고 당황을한다. 함수를 따라 들어가 보아야 동작을 이해할 수 있다.

getSum()은 이름만 보아도 해당 함수가 덧셈을 수행한다는 정보를 얻을 수 있다.

바로 이런 점에서 차이가 발생한다.

 

생각보다 클린 코드는 매우 중요하다. 우리는 혼자 일하지 않기 때문이다.

'클린 코드'를 적용하지 않으면 개발 속도는 빠를 수 있으나, 유지보수 시간이 길어진다.

적용하면 개발 속도는 더딜 수 있으나, 추후 90%를 차지할 유지보수 시간이 짧아진다.

어떤 것이 이익일지 우리는 바로 알 수 있다.

 

 


2. 구현 방법

"작은 노력에서 시작된다."

 

 

범위 : 네이밍 지정, 함수 작성

  • 네이밍 - 변수나 함수명엔 의도를 보이자.
    int w; //X
    html(); //X
    
    int weatherToday; //O
    getResponseBody(); //O
     
  • 네이밍 - 길이와 상관없다. 명료하게 쓰자.
    string m; //X
    string i; //X
    getRegistedUser(); //X
    
    string macAddr; //O
    string ipAddr; //O
    getUser(); //O
     
  •  네이밍 - 타입이 이름에 꼭 들어갈 필요 없다.
    int nDate; //X
    string sName; //X
    
    int date; //O
    string name; //O
     
  •  네이밍 - 클래스명은 명사, 메서드명은 동사를 활용하자.
    class User{
    private:
        int age = 0;
    public:
        setAge(int age) //...
    }
     
  • 네이밍 - 헷갈릴만한 단어는 빼고, 같은 단어는 통일해서 쓰자.
    int l = 1; //X => 헷갈리는 변수명은 OUT!
    int O = 0; //X => 헷갈리는 변수명은 OUT!
    int x = 0; //X
    Master.printInfo(...); //X => Info와 Data 혼용
    Slave.printData(...); //X => Info와 Data 혼용
    
    int positionX = 3; //O
    Master.printInfo(...); //O
    Slave.printInfo(...); //O
  • 함수 - 짧아야 한다.
    string getIpAddr(char mac[MAC_LEN]){ //X
        string ip = "";
        ...
        ARP arp = {};
        arp.hardwareType = 0x0000
        ...
        return ip
    }
    
    string getIpAddr(char mac[MAC_LEN]){ //O
        string ip = sendArp(mac);
        return ip;
    }
    ...
  • 함수 - 하는 일은 하나다.
    int isTmpFolder(string path){
    	...
        checkPath(); //O => 함수는 본질의 단 하나 역할만 수행
        setFlag(); //X
        writeFile(); //X
        ...
    }
  • 함수 - 수준을 맞추자. 출력은 출력하는 곳에서..
    class User{
    private:
    	int Age;
    public:
    	int setAge(int age){
    		Age = age;
    		std::cout << Age << std::endl; //X
    	}
    	int getAge(){ //O
    		return Age;
    	}
    }
  • 함수 - 인자는 적을 수록 좋다.
    int sendData(int mac[MAC_LEN], int ip[IP_LEN], char* dev, ...) // X
    int sendData(Packet packet); // O
  • 함수 - 반복하지 마라.
    int setPlace(Place place){ //X
    	if(place.positionX == '1'){
    		place.positionY = '2';
    		place.positionX = '3';
    	}else if(place.positionX == '3'){
    		place.positionY = '2';
    		place.positionX = '3';
    	}
    }
    
    int setPlace(Place place){ //O
    	if(place.positionX == '1' || place.positionX == '3')
    		setPosition(2,3)
    }

 


3. 마치며

"좋은 그림과 나쁜 그림은 누구든 구별할 수 있다.

그러나 직접 그려보지 않으면 좋은 그림을 그리진 못한다."

 

 

 어쩌면 그냥 무시하고 지나갈 수 있는 이론이나, 한번 적용해보면 추후 코드를 다시 볼 때 크게 와닿을 것이다.

본인도 아직 부족해 계속해서 공부해나가고자 한다. 저자는 이론만으론 클린코드를 작성할 수 없다고 말한다.

우리는 계속해서 작성했던 코드를 보완하고, 보완해나가야 한다.

글을 읽는 것에서 끝나지 않고, 본인의 코드를 찾아 변수명을 바꿔보는 작은 행동에서 큰 변화는 시작될 것이라 믿는다.


아직 3장까지 밖에 읽지 못했지만 완독을 하고 다시 한번 정리해볼 계획이다.

+ 피드백 및 저작권에 대한 문의 시 즉시 수정할 예정이다.

반응형

+ Recent posts