생활
클래스 어떻게 만들며, 네이밍은 어떻게 해야 되나요?
C++ 프로젝트를 해야 되는데 클래스가 뭔가요?
어떻게하면 제대로 클래스를 만드나요?
클래스 네이밍은 어떻게 해야 효율적이며, 다른 사람이 찾기가 쉽나요?
쉬운 네이밍 방법이 있나요?.
3개의 답변이 있어요!
안녕하세요~! 아하(Aha) 지식답변자 다라닝입니다.
질문하신 내용에 대하여 아래와 같이 답변 드립니다.클래스는 묶음이죠.
너무 거창하게 의미 부여하지 않으셔도 되구요
네이밍은 개인차가 있지만 그 안에서 이루어질 모든것들을 넓은의미로 담고 있어야합니다.
일반적으로 무엇에대한행위 를 많이 표현하구요.
대소문자를 활용하여 지어주면 나중에 본인이 볼때도 한눈에 잘 들어옵니다.
(클래스명 근처에 주석달아주는 습관도 좋습니다)
부족하지만 도움이 되셨기를 바라며 추가적인 문의가 필요하시면 답변 부탁드려요!C++ 클래스의 쉬운 네이밍 방법이라면 함수, 변수, 파일 이름의 경우는 축약해서 쓰지 않는다.
(타입과 변수는 명사로 함수는 명령 동사로 쓰는 것이 좋다고 합니다.)
그리고 이름은 설명적으로 쓰되 축약하지 않는 것이 좋습니다. 굳이 축약을 해야한다면 잘 알려진 약어나 또는 프로젝트 내에서
서로 협의 하에 쓰는 축약어를 쓰는게 좋습니다.
그리고 클래스란 C언어에서 보았던 구조체의 확장된 것이라고 보면 됩니다.
C언어에서의 구조체는 타입이 다른 변수의 집합체였다고 보면, C++에서의 클래스는 C언어의 구조체 + 함수까지 포함된 것이라 보면 됩니다.
C++의 클래스 정의는 이렇습니다.
Class 클래스의 이름
{
접근 제어 지시자:
멤버 변수의 타입 멤버 변수의 이름;
};
이 정의를 기반으로 이용하시면 될 것이라 봅니다.
어떤 개발언어든 프로그래밍에 표준이랄까 최초 제안되는 사례는 있습니다만, 집단과 개인의 성향에 따라, 상황에 따라 표준 규약은 달라질 수 있습니다.
아래는 통상적인 C++의 네이밍 규칙에 대한 내용입니다. 구글링해보면 찾으실 수 있으나 여기 남겨봅니다.
Coding Style Guide의 Naming Rule을 따르게 되면 일반적으로 더 나은 가독성이 제공되고 이름 충돌로 인한 오류 발생을 최소화 시킬 수 있게 된다. 이 섹션에서는 C++ 명명규칙을 표준화하며 공통 규칙은 아래와 같다.
공통규칙 1. 이름은 가능한 [설명적]이어야 하므로 축약 하지 않는다..
공통규칙 2. 모든 이름은 영문 알파벳, 숫자, 밑줄(_) 만을 사용하여 짓는다.
공통규칙 3. 모든 이름의 길이는 두 자 이상이어야 한다. 단, 반복문의 카운트 변수 제외.
공통규칙 4. “tmp”와 같이 정확한 용도를 파악하기 힘든 이름은 사용하지 않는다.
General Naming Rule
타입과 변수는 명사로, 함수는 ‘명령’동사로 짓는다.
이름 짓는 방법: 코드를 즉시 이해 하는 것이 중요 하므로 수평공간을 절약 하지 않는다.
예)
int num_errors; // good
int numcompltedconnections; // good
int n; // bad - 의미가 모호함
int nerr; // bad - 모호한 약어
int ncompconns; // bad - 모호한 약어
약어: 프로젝트 외에 잘 알려져 있지 않으면 약어를 사용하지 않는다.
예)
Good:
>> int numdnsconnections; // 많은 사람들이 'DNS'을 알고 사용 하고 있으므로 사용가능
>> int pricecountreader; // 많은 사람이 모르고 있으므로 price_count를 사용
Bad:
>> int wgc_connection; //프로젝트에서만 알려진 약어
>>int pc_reader; //pc라는 것은 많은 것을 의미하므로 안됨
절대로 글자를 빠트려 축약 하지 않는다.
예)
int error_count; //good
int error_cnt; //bad
File Naming Rule
파일 이름은 모두 소문자 여야 하고, 밑줄()이나 대시(-)를 포함 할 수 있으나 보통 밑줄()을 사용함
예)
myusefulclass.cpp
myusefulclass.cpp
myusefulclass_unittest.cpp
C++파일은 cpp로 헤더는 .h로 마쳐야 한다.
일반적으로 이름은 매우 한정적으로 만든다
예)
logs.h보다 httpserverlogs.h를 사용한다
인라인 함수를 일반적으로 매우 짧은 경우 .h 파일에 넣지만, 코드가 길어질 경우 -inl.h로 끝나는 별도 파일에 넣는다.
예)
url_talbe.h //class declaration
url_talbe.cpp //class definition
url_talbe-inl.h //inline functions
Type Naming Rule
타입이름은 대문자로 시작하고 새 단어를 시작할 때 마다 대문자를 쓰며, 밑줄은 사용하지 않는다.
클래스, 구조체, 타입정의, 열거형과 같은 모든 타입이름은 이름규칙이 같다
예)
//classes and structs
Class UrlTable{….
Class UrlTableTester{…
Struct UrlTableProperties{…
//typedefs
Typedef hash_map PropertiesMap;
//enum
Enum UrlTableErrors{…
Class and Interface(Pure Abstract Class) Type 이름
인터페이스 명은 클래스 명과 동일한 규칙을 적용한다.
추상 클래스(Abstract Class)의 구분은 따로 하지 않는다.
클래스 명은 명사 또는 명사구를 사용한다.
클래스의 역할을 정확히 표현할 수 있는 단어를 택하되 너무 길어지지 않도록 주의한다. (표현력과 길이 사이의 적절한 타협점을 찾도록 할 것)
일반적으로 축약어를 사용하지 않으며 광범위하게 사용되는 축약어만을 허용한다.
각 단어의 첫 문자는 대문자로 하며 그 외에는 소문자 사용을 원칙으로 한다.
예)
ClassLoader
SecurityManager
Thread
BufferedInputStream
HttpConnection
Variable Naming Rule
변수 이름은 모두 소문자 이며 단어 사이에 밑줄을 쓴다.
클래스 멤버 변수는 마지막에 밑줄을 붙인다.
구조체 데이터 멤버 이름규칙은 일반 변수와 같으나 클래스 데이터 멤버와 달리 끝에 밑줄을 붙이지 않는다.
예)
일반변수: string table_name;
클래스 볌수: string tablename;
구조체 변수:
Struct UrlTableProperties{
string name;
int num_entries;
}
다음의 경우를 제외하고 한 문자로 된 변수의 선언을 금한다.
반복문 내에서 사용하는 카운트 변수
예)
for(inti=0;i<MAX_LENGTH;i++){
…
}
try-catch 문의 Exception 파라미터 명
예)
try{
…
}catch(Exceptione){
…
}
- Graphics 객체는 일반적으로 ‘g’로 사용
예:
publicvoidpaint(Graphicsg){
…
}
Constant Naming Rule
const예약어를 사용하고 K로 시작하고 각 단어는 대문자로 시작 한다.
예)
const int kDaysInAWeek = 7;
Method Naming Rule
메소드 명은 동사 또는 동사구를 사용한다.
메소드의 기능을 정확히 표현할 수 있는 단어를 택하되 너무 길어지지 않도록 주의한다. (표현력과 길이 사이의 적절한 타협점을 찾도록 할 것)
일반 함수는 대문자를 섞어 쓰며 접근자와 변경자는 변수 이름과 일치 시킨다.
예)
à 일반 함수
AddTableEntry();
DeleteUrl();
OpenFileOrDie();
à 접근자와 변경자(get,set함수): 자바스타일을 따라 간다.
Class MyClass{
public:
….
int getnumentries() const {return numentries;}
void setnumentries(int numentries){numentries = numentries;}
private:
int numentries;
};
Namespace Naming Rule
모두 소문자를 이용하여 작성하고, 프로젝트 이름에 기초하며 가능한 디렉터리 구조도 바탕으로 한다.
Enumerator Naming Rule
열거형 이름은 상수 또는 매크로 이름 규칙중 하나를 선택한다. 즉, EnumName또는 ENUM_NAME중 하나를 선택한다.
Exception and Error Class Type 이름
“2.3 Class and Interface Type Names”과 동일한 규칙을 적용한다.
Exception 클래스의 이름은 반드시 ‘Exception’으로 끝맺는다.
Error 클래스의 이름은 반드시 ‘Error’로 끝맺는다.
예)
TimeoutException
VerifyError
Header 파일
C++에서는 Header파일은 매우 중요하다. 컴파일러에게 Class의 형태 및 접근방식 및 선언에 대한 에러를 체크 할 수 있게 한다. 만약 헤더가 중복으로 선언 되었을 경우 오류를 발생하므로 여기에서는 헤더의 중복 선언을 방지하고 의존성문제의 해결을 위한 가이드를 제시한다.
Define Guard
헤더 include시 중복 선언을 막기 위해 #define을 이용하여 선언 하는 방법
<project><path><file>H 형태로 선언 한다.
예)
foo 프로젝트에 foo/src/bar/baz.h가 있다면,
#ifndef FOOBARBAZH
#define FOOBARBAZH
{
개발 내용
}
#endif
Header File의 의존성
헤더 파일을 포함하면 의존성을 가지므로 그 헤더 파일이 바뀔 때 마다 코드도 새로 컴파일 해야 한다.
전방 선언으로 충분하면 #include를 사용하지 않아야 한다. 전방선언을 할 경우 컴파일 속도 향상 및 참조하려는 헤더파일이 변경되어도 재 컴파일이 이루어 지지 않는다. 의존성에서도 이득이 생긴다.
전방선언: 참조하려는 클래스를 자신의 헤더에 #include 하지 않고 몸체(cpp)에 선언 하는 방식. Header파일에는 [Class 클래스명;]으로 선언한다.
주의사항: 참조하려는 클래스를 포인터 형으로 선언하는 경우만 사용 할 수 있다. 클래스의 상속, 포인터 형이 아닌 객체를 생성 하는 경우에는 전방선언 방식을 사용 할 수 없다.
이름과 포함 순서
가독성을 높이고 숨은 의존성을 피하기 위해 다음과 같은 순서를 사용한다.
자기 자신의 헤더파일
C시스템 헤더파일
C++ 시스템 헤더파일
그 외 라이브러리 헤더파일
프로젝트에 속한 헤더파일
Inline Functions
함수 내용이 10줄 이하일 때만 인-라인 정의
단점: 인-라인 함수의 방식이 코드를 컴파일 과정에서 넣는 방식이라 인-라인을 과도하게 사용하면 프로그램 성능이 떨어짐.
함수를 인-라인으로 선언 하더라도 항상 인-라인이 되는 것은 아니다.
가상 또는 재귀 함수는 일반적으로 인-라인이 되질 않는다.
필요에 따라 –inl.h를 정의하여 사용한다.
Class 생성자
일반적으로 생성자는 멤버변수를 초기화 하는 일만 해야 한다. 모든 복잡한 초기화는 명시적으로 Init() 메소드를 만들어서 사용 해야 한다. 왜냐하면, 생성자는 오류를 알리기가 쉽지 않고, 생성 도중 무언가 실패하면 초기화를 마치지 않은 객체가 생기므로 상태를 알 수 없다. 여기에서는 위와 같은 문제의 발생을 줄이기 위하여 생성자에 대해 정의 한다.
기본 생성자
클래스에 멤버 변수를 정의 했으나 다른 생성자가 없다면 기본 생성자를 정의해야 한다. 그렇지 않으면 컴파일러에서 알아서 처리 하지만 복사 생성자등 이상하게 생성 할 가능성이 크다
기본 생성 자에서는 되도록 객체 상태를 일관되고 유효하게 초기화 하는 것이 좋다.
명시적 생성자(Explicit Constructors)
일반적으로 인자가 하나인 생성자는 변환에 사용 할 수 있다.
예를 들어 Foo::Foo(int num)을 정의 하고 Foo를 인자로 받는 함수에 int을 전달 하면, Foo(int num)생성자를 호출해 int값을 Foo로 변환한 다음 함수에 전달한다. 이는 편리하지만 의도하지 않게 변환하고 새 객체를 생성하므로 문제를 일으킬 수도 있다. 생성자를 explicit로 선언하면 변환할 때 암시적으로 호출하는 것을 막을 수 있다.
장점: 바람직 하지 않은 변환을 피할 수 있다.
단점: 없음.(즉 인수가 하나일 경우 explicit를 사용해야 한다.)
복사 생성자
복사생성자와 대입 연산자는 필요할 때만 사용하고 사용 하지 않을 때는 *DISALLOWCOPYAND_ASSIGN*(정의된 메크로)을 사용해 비활성화 한다.
장점
객체를 쉽게 복사 할 수 있고, CopyFrom형식보다 더 효율적일 수 있다.
어떤 상황에서는 컴파일러에서 이 과정을 건너 뛸 수 있다.
단점
객체를 암시적으로 복사 하는 것은 많은 버그와 성능 문제를 만들 수 있다.
가독성을 떨어뜨리고, 참조에 비해 값으로 전달 할 때 어떤 객체를 전달하는지 추적하기 어렵다.
결론
복사 할 수 있어야 하는 클래스는 적다. 그러므로 필요하면 CopyFrom이나 Clone과 같은 복사 메소드를 제공해야 한다. 그래야 암시적으로 호출 되는 것을 막을 수 있다.
구조체 vs. 클래스
데이터를 전달하는 수동 객체일 때만 struct를 사용하고 그 외에는 모두 class를 사용한다.
C++에서 struct와 class 키워드는 거의 동일하므로 각 키워드에 내포된 의미를 추가해 정의 하는 데이터 타입에 따라 적절한 키워들 사용 한다.
Struct
데이터를 전달 하는 수동 객체에만 사용하고, 관련 상수는 있을 수 있으나 데이터 멤버를 접근하거나 설정하는 것 외 다른 기능은 없어야 한다. 필드에 접근 하거나 설정 할 때는 메소드를 호출하지 말고 해당 필드에 직접 접근해 처리한다.
메소드는 행위를 제공하지 않아야 하고 생성자, 소멸자, Initialize(), Reset(), Validate() 등 데이터 멤버를 설정 하는 데만 써야 한다.
더 많은 기능이 필요하면 Class를 사용해야 한다.
어떤 형식으로 써야 할 지 애매모호 할 경우 class를 사용한다.
주석 처리 형태
주석 처리 형태는 c++에서 지원하는 /../, //을 사용하나 주석의 형태는 Doxygen에서 지원 하는 Doxgen Java Style 형태를 사용하여 가독성을 높인다.
기본 원칙
주석작성의 목적은 자기 자신뿐만 아니라 다른 사람을 위한 작업이므로 자기만 알아 볼 수 있는 약어나 농담 사용을 금한다.
주석은 모국어로 기입한다.
주석은 항상 최신으로 유지되어야 한다.
헤더파일의 손상을 최소화 한다. 헤더파일은 개발자가 가장 자주 접근하는 파일이다. 무분별한 주석 추가로 인하여 헤더파일 자체의 분석능력을 감소 시키면 안 된다.
함수의 주석은 동사로 끝을 맺고 변수나 인자, 객체의 주석은 명사로 끝을 맺는다.
함수는 행위를 나타내고(동사)
변수는 객체를 나타내기 때문(명사)
주석 입력 작업의 목적은 코드를 알아보기 좋게 하는 작업 이므로 모든 사항을 지나치게 철저하게 지킬 필요는 없다.
소스파일의 최상단에는 파일명, 날짜, 제작자, 설명을 명기한다.
주석의 방식은 /* …/인 것에 주의 한다.
.h, -inl.h, .cpp등 모든 소스 파일에 표기한다.
예)
/**
@file comment.h
@date 2013-12-15
@author 개발자A(developer@company.com) 프로젝트명
@brief 주석을 소개 하기 위한 예제 코드
@warning 문서화를 위해 잘 작성 해야 한다.
*/
Class Comment{
…
}
“/ … /” Style Comment
코드의 일부분을 실행되지 않도록 불활성화.
부득이하게 코드 라인 처음, 또는 중간에 주석 추가.
이상의 경우 외에는 사용을 금한다.
“//” Style Comment
한 라인의 주석을 처리한다.
여러 라인의 설명을 나타낼 경우 / / 형태를 사용하는 대신 각 라인을 // 로 시작한다.
// 이후 한 칸의 공백을 유지한다.
“///<… “ Style Comment
클래스/ 구조체 멤버 변수의 주석에 사용
예)
int numerrors; ///< 에러의 개수
클래스 맴버 함수의 헤더에 사용하고, 리턴값 및 인자에 대한 설명이 필요하면 구현부분에 추가 한다.
예)
.h파일
int Sum(const int a, const int b); ///< 더하기
.cpp 파일
/**
@return a+b의 결과
*/
int Comment::Sum(const int a, const int b){
….
}
“///” Style Comment
일반 함수는 클래스 맴버함수와 동일하나, ///를 이용하여 주석을 작성 한다.
예)
///멤버를 생성하는 함수
extern BOOL CreateMember(const int id);