developer tip

가장 좋아하는 C ++ 코딩 스타일 관용구는 무엇입니까?

copycodes 2020. 12. 2. 21:29
반응형

가장 좋아하는 C ++ 코딩 스타일 관용구는 무엇입니까?


가장 좋아하는 C ++ 코딩 스타일 관용구는 무엇입니까? 나는 중괄호를 넣는 위치, 키워드 뒤에 공백이 있는지, 들여 쓰기 크기 등과 같은 스타일이나 코딩 타이포그래피에 대해 묻고 있습니다. 이것은 항상 delete[].

다음은 내가 가장 좋아하는 것 중 하나의 예입니다. C ++ 클래스 이니셜 라이저에서는 구분 기호를 뒤가 아닌 줄 맨 앞에 둡니다. 이렇게하면이를 최신 상태로 유지하기가 더 쉽습니다. 또한 버전 간의 소스 코드 제어 차이가 더 깨끗하다는 것을 의미합니다.

TextFileProcessor::
TextFileProcessor( class ConstStringFinder& theConstStringFinder ) 

    : TextFileProcessor_Base( theConstStringFinder )

    , m_ThreadHandle  ( NULL )
    , m_startNLSearch (    0 )
    , m_endNLSearch   (    0 )
    , m_LineEndGetIdx (    0 )
    , m_LineEndPutIdx (    0 )
    , m_LineEnds      ( new const void*[ sc_LineEndSize ] )
{
    ;
}

열거 형을 만들 때 의미있는 이름으로 액세스 할 수 있도록 네임 스페이스에 넣습니다.

namespace EntityType {
    enum Enum {
        Ground = 0,
        Human,
        Aerial,
        Total
    };
}

void foo(EntityType::Enum entityType)
{
    if (entityType == EntityType::Ground) {
        /*code*/
    }
}

편집 : 그러나이 기술은 C ++ 11에서 사용되지 않습니다. 대신 범위가 지정된 열거 ( enum class또는로 선언 됨 enum struct)를 사용해야합니다. 형식이 더 안전하고 간결하며 유연합니다. 이전 스타일 열거를 사용하면 값이 외부 범위에 배치됩니다. 새 스타일 열거를 사용하면 enum class이름 범위 내에 배치됩니다 .
범위가 지정된 열거를 사용하여 재 작성된 이전 예제 ( 강력한 유형의 열거 라고도 함 ) :

enum class EntityType {
    Ground = 0,
    Human,
    Aerial,
    Total
};

void foo(EntityType entityType)
{
    if (entityType == EntityType::Ground) {
        /*code*/
    }
}

범위가 지정된 열거를 사용하면 다른 중요한 이점이 있습니다. 암시 적 캐스트 부재, 가능한 앞으로 선언 및 사용자 지정 기본 형식 (기본값 int아님) 을 사용할 수있는 기능 입니다.


RAII : 리소스 획득은 초기화입니다.

RAII는 가장 중요한 관용구 일 수 있습니다. 리소스를 개체에 매핑하여 해당 개체가 선언 된 범위에 따라 수명이 자동으로 관리되도록하는 것이 아이디어입니다.

예를 들어, 파일 핸들이 스택에서 선언 된 경우 함수 (또는 루프 또는 내부에서 선언 된 범위)에서 반환되면 암시 적으로 닫혀 야합니다. 동적 메모리 할당이 클래스의 멤버로 할당 된 경우 해당 클래스 인스턴스가 소멸 될 때 암시 적으로 해제되어야합니다. 등등. 모든 종류의 리소스 (메모리 할당, 파일 핸들, 데이터베이스 연결, 소켓 및 획득 및 해제해야하는 기타 모든 종류의 리소스)는 해당 RAII 클래스 내에 래핑되어야하며 수명은 해당 범위에 따라 결정됩니다. 선언.

이것의 한 가지 주요 이점은 C ++가 제어가 해당 범위를 벗어나는 방법에 관계없이 객체가 범위를 벗어날 때 소멸자가 호출되도록 보장한다는 것입니다 . 예외가 발생하더라도 모든 로컬 개체는 범위를 벗어나므로 관련 리소스가 정리됩니다.

void foo() {
  std::fstream file("bar.txt"); // open a file "bar.txt"
  if (rand() % 2) {
    // if this exception is thrown, we leave the function, and so
    // file's destructor is called, which closes the file handle.
    throw std::exception();
  }
  // if the exception is not called, we leave the function normally, and so
  // again, file's destructor is called, which closes the file handle.
}

함수에서 나가는 방법과 파일이 열린 후 어떤 일이 발생하는지에 관계없이 파일을 명시 적으로 닫거나 해당 함수 내에서 예외 (예 : try-finally)를 처리 할 필요가 없습니다. 대신 파일이 범위를 벗어날 때 파괴되는 로컬 개체에 연결되어 있기 때문에 파일이 정리됩니다.

RAII는 SBRM (Scope-Bound Resource Management)이라고도 잘 알려져 있지 않습니다.

또한보십시오:

  • ScopeGuard를 사용하면 코드에서 "예외가 발생하는 경우 '실행 취소'작업 ..을 자동으로 호출"할 수 있습니다.

카피 스왑

copy-swap 관용구는 예외로부터 안전한 복사를 제공합니다. 올바른 복사기와 스왑이 구현되어야합니다.

struct String {
  String(String const& other);

  String& operator=(String copy) { // passed by value
    copy.swap(*this); // nothrow swap
    return *this; // old resources now in copy, released in its dtor
  }

  void swap(String& other) throw() {
    using std::swap; // enable ADL, defaulting to std::swap
    swap(data_members, other.data_members);
  }

private:
  Various data_members;
};
void swap(String& a, String& b) { // provide non-member for ADL
  a.swap(b);
}

ADL (Argument Dependent Lookup)을 사용하여 스왑 메서드를 직접 구현할 수도 있습니다 .

이 관용구는 자체 할당을 처리하고 [1] , 강력한 예외 보장을 보장하고 [2] , 작성하기가 매우 쉽기 때문에 중요합니다.


[1] 자체 할당이 가능한 한 효율적으로 처리되지는 않지만 드물게 발생하므로 절대 발생하지 않으면 실제로 더 빠릅니다.

[2] 예외가 발생하면 객체 ( *this) 의 상태 가 수정되지 않습니다.


CRTP : 흥미롭게 반복되는 템플릿 패턴

CRTP 는 클래스를 기본 클래스에 템플릿 매개 변수로 전달할 때 발생합니다.

template<class Derived>
struct BaseCRTP {};

struct Example : BaseCRTP<Example> {};

기본 클래스 내에서 단순히 캐스팅 ( static_cast 또는 dynamic_cast 작업) 을 통해 파생 유형으로 완성 된 파생 인스턴스를 확보 할 수 있습니다 .

template<class Derived>
struct BaseCRTP {
  void call_foo() {
    Derived& self = *static_cast<Derived*>(this);
    self.foo();
  }
};

struct Example : BaseCRTP<Example> {
  void foo() { cout << "foo()\n"; }
};

실제로 call_foo파생 클래스의 멤버에 대한 전체 액세스 권한과 함께 파생 클래스 삽입 되었습니다.

특정 사용 예를 자유롭게 편집하고 다른 SO 게시물에 추가하십시오 .


pImpl : 구현에 대한 포인터

pImpl 관용구는 구현에서 클래스의 인터페이스를 분리하는 매우 유용한 방법입니다.

일반적으로 클래스 정의에는 멤버 변수와 메서드가 포함되어야하므로 너무 많은 정보가 노출 될 수 있습니다. 예를 들어 멤버 변수는 모든 곳에 포함하고 싶지 않은 헤더에 정의 된 유형일 수 있습니다.

windows.h헤더는 여기에 대표적인 예이다. HANDLE클래스 내부에 하나 또는 다른 Win32 유형 을 래핑 하려고 할 수 있지만 클래스가 사용되는 모든 곳 HANDLE을 포함하지 않고는 클래스 정의에를 넣을 수 없습니다 windows.h.

이 솔루션은 다음 만드는 것입니다 P의 rivate의 IMPL의 ementation 또는 P ointer 대 IMPL의 모든 구성원 방법을 클래스의 ementation을, 대중 구현 저장소에게 개인 일 만 포인터를하자, 그리고 전달합니다.

예를 들면 :

class private_foo; // a forward declaration a pointer may be used

// foo.h
class foo {
public:
  foo();
  ~foo();
  void bar();
private:
  private_foo* pImpl;
};

// foo.cpp
#include whichever header defines the types T and U

// define the private implementation class
class private_foo {
public:
  void bar() { /*...*/ }

private:
  T member1;
  U member2;
};

// fill in the public interface function definitions:
foo::foo() : pImpl(new private_foo()) {}
foo::~foo() { delete pImpl; }
void foo::bar() { pImpl->bar(); }

의 구현 foo은 이제 공용 인터페이스에서 분리되어

  • 클래스가 사용될 때 이러한 종속성이 존재하지 않아도 다른 헤더의 멤버 및 유형을 사용할 수 있습니다.
  • 클래스를 사용하는 코드를 다시 컴파일하지 않고도 구현을 수정할 수 있습니다.

클래스의 사용자는 단순히 클래스 구현에 대한 구체적인 내용을 포함하지 않는 헤더를 포함합니다. 모든 구현 세부 정보는 foo.cpp.


나는 '열'에서 코드 / 초기화를 정렬하는 것을 좋아합니다 ... '열'모드가 가능한 편집기로 편집 할 때 매우 유용하며 읽기가 훨씬 더 쉬운 것 같습니다 ...

int myVar        = 1;    // comment 1
int myLongerVar  = 200;  // comment 2

MyStruct arrayOfMyStruct[] = 
{   
    // Name,                 timeout,   valid
    {"A string",             1000,      true    },   // Comment 1
    {"Another string",       2000,      false   },   // Comment 2 
    {"Yet another string",   11111000,  false   },   // Comment 3
    {NULL,                   5,         true    },   // Comment 4
};

대조적으로, 위와 같이 들여 쓰기 및 형식이 지정되지 않은 동일한 코드가 나타납니다 ... (내 눈에 읽기가 조금 더 어렵습니다)

int myVar = 1; // comment 1
int myLongerVar = 200; // comment 2

MyStruct arrayOfMyStruct[] = 
{   
    // Name, timeout, valid
    {"A string", 1000, true},// Comment 1
    {"Another string", 2000, false }, // Comment 2 
    {"Yet another string", 11111000,false}, // Comment 3
    {NULL, 5, true }, // Comment 4
};

퍼블릭 탑-프라이빗 다운

겉보기에 작은 최적화 였지만,이 컨벤션으로 전환 한 이후로, 특히 42 년 동안 수업을 보지 않은 후 수업을 파악할 수있는 더 재미있는 시간을 갖게되었습니다.

빈번한 관심 지점에서 지루한 부분까지 일관된 멤버 가시성을 갖는 것은 특히 코드가 자체 문서화되어야 할 때 매우 유용합니다.

(qt 사용자를위한 참고 사항 : 슬롯은 슬롯이 아닌 멤버 함수처럼 호출 할 수 있어야하고 슬롯이 아닌 슬롯과 구별 할 수 없기 때문에 신호보다 먼저 나타납니다.)

  • 공개, 보호, 비공개
  • 그런 다음 Factory, ctor, dtor, copying, swapping
  • 그런 다음 클래스의 인터페이스 마지막으로 별도의 private:섹션에서 데이터가 제공됩니다 (이상적으로는 단순 포인터 만).

이 규칙은 클래스 선언을 깔끔하게 유지하는 데 문제가있는 경우에도 도움이됩니다.

class Widget : public Purple {
public:
    // Factory methods.
    Widget FromRadians (float);
    Widget FromDegrees (float);

    // Ctors, rule of three, swap
    Widget();
    Widget (Widget const&);
    Widget &operator = (Widget const &);
    void swap (Widget &) throw();

    // Member methods.
    float area() const;

    // in case of qt {{ 
public slots:
    void invalidateBlackHole();

signals:
    void areaChanged (float);
    // }}

protected:    
    // same as public, but for protected members


private:    
    // same as public, but for private members

private:
    // data
    float widgetness_;
    bool  isMale_;
};

에서 if문, 어려운 조건이있을 때, 당신은 명확하게 각 조건은 들여 쓰기를 사용하는 수준 표시 할 수 있습니다.

if (  (  (var1A == var2A)
      || (var1B == var2B))
   && (  (var1C == var2C)
      || (var1D == var2D)))
{
   // do something
}

내가하지만 즐겨 찾기되지 않습니다 코드가 수정 :

  1. 탭-모드 8 공간에서 탭에 항상 동의하지 않기 때문에 많은 IDE 및 코드 검토 도구에서 정렬 오류가 발생합니다.
  2. 80 열보다 긴 줄-직시하자면 짧은 줄이 더 읽기 쉽습니다. 내 두뇌는 줄이 짧은 한 대부분의 코딩 규칙을 구문 분석 할 수 있습니다.
  3. 후행 공백이있는 줄-git은 공백 오류 로 불만 을 표시합니다. 이는 diff에서 빨간색 얼룩으로 표시됩니다.

다음은 문제가되는 파일을 찾는 한 줄입니다.

git grep -I -E '<tab>|.{81,}|  *$' | cut -f1 -d: | sort -u

<tab>탭 문자는 어디에 있습니까 (POSIX regexp는 \ t를 수행하지 않음)


re : ididak

긴 문을 너무 많은 짧은 줄로 나누는 코드를 수정합니다.

현실을 직시합시다 : 그것은 더 이상 90 년대가 아닙니다. 회사가 코더를위한 와이드 스크린 LCD를 감당할 수 없다면 더 나은 일자리를 얻어야합니다. :)


컴파일 타임 다형성

(런타임 다형성과 대조되는 구문 다형성 및 정적 다형성이라고도합니다.)

템플릿 함수를 사용하면 공통 기본 클래스를 도입하지 않고도 유형 생성자에 의존하는 코드를 작성하고 매개 변수화 된 유형 패밀리의 서명을 호출 할 수 있습니다.

저서 Elements of Programming 에서 저자는 이러한 유형의 처리를 추상 속이라고 합니다. 개념 C ++ 같은 사양을 강제하지 않지만 하나, 같은 유형의 매개 변수에 대한 요구 사항을 지정할 수 있습니다.

두 가지 간단한 예 :

#include <stdexcept>

template <typename T>
T twice(T n) {
  return 2 * n;
}

InIt find(InIt f, InIt l,
          typename std::iterator_traits<InIt>::reference v)
{
  while (f != l && *f != v)
    ++f;
  return f;
}   

int main(int argc, char* argv[]) {
  if (6 != twice(3))
    throw std::logic_error("3 x 2 = 6");

  int const nums[] = { 1, 2, 3 };
  if (nums + 4 != find(nums, nums + 4, 42))
    throw std::logic_error("42 should not have been found.");

  return 0;
}

twice이항 *연산자가 정의 된 일반 유형으로 호출 할 수 있습니다 . 마찬가지로 find()비교 가능한 모든 유형과 해당 모델 Input Iterator로 호출 할 수 있습니다 . 하나의 코드 세트가 다른 유형에서 유사하게 작동하며 공유 기본 클래스가 보이지 않습니다.

Of course, what's really going on here is that it's the same source code being expanded into various type-specific functions at template instantiation time, each with separate generated machine code. Accommodating the same set of types without templates would have required either 1) separate hand-written functions with specific signatures, or 2) runtime polymorphism through virtual functions.


Template and Hook

This is a way to handle as much as possible in a framework and give a door or hook for customization by users of a framework. Also known as Hotspot and Template Method.

class Class {
  void PrintInvoice();     // Called Template (boilerplate) which uses CalcRate()
  virtual void CalcRate() = 0;  // Called Hook
}

class SubClass : public Class {
  virtual void CalcRate();      // Customized method
}

Described by Wolfgang Pree in his book Design Patterns for Object-Oriented Software Development.


if/while/for parenthesized expression(s) WITH a space separator

if (expression)  // preferred - if keyword sticks out more

vs.

if(expression)  // looks too much like a void function call

I guess this implies that I like my function calls to NOT have a space separator

foo(parm1, parm2);

After working with someone who was partly blind - and at his request - I switched to using many more spaces. I didn't like it at the time, but now I prefer it. Off the top of my head, the only place where there isn't whitespace between identifiers and keywords and whatnot is after a function name and before the following parentheses.

void foo( int a, int b )
{
  int c = a + ( a * ( a * b ) );
  if ( c > 12 )
    c += 9;
  return foo( 2, c );
}

I don't know if it qualifies as an idiom, exactly, but quite a bit of heavy-duty template programming depends (often heavily) on SFINAE (substitution failure is not an error). A couple of the answers to a previous question have examples.


I really like putting a small statement on the same line as an if

int myFunc(int x) {
   if(x >20) return -1;
   //do other stuff ....
}

Not sure if this counts as an idiom, but I tend to use doxygen-style inline comments even when the project isn't -yet- using doxygen...

bool MyObjects::isUpToSomething() ///< Is my object up to something 

(aside. my comments are not usually quite that lame.)


It's useful to put function names on a new line, so you can grep like

grep -R '^fun_name' .

for them. I've seen that style used for a loads of GNU projects and like it:

static void
fun_name (int a, int b) {
    /* ... */
}

Document the return values on the function line, so they are very easy to find.

int function(void) /* return 1 on success, 0 on failure */ 
{
    return 1;
};

Write each method or function argument on a separate line such that it can be easily commented.

int ReturnMaxValue(
    int* inputList,   /* the list of integer values from which to get the maximum */
    long size,        /* count of the number of integer values in inputList */
    char* extraArgs   /* additional arguments that a caller can provide.    */
)

I'd suggest PIMPL or as James Coplien originally called it "Handle Body".

This idiom allows you to completely decouple interface from implementation. When working on the rewrite and re-release of a major CORBA middleware component, this idiom was used to completely decouple the API from the implementation.

This practically eliminated any possibility reverse engineering.

An excellent resource for C++ idioms is James Coplien's excellent book "Advanced C++ Programming Styles and Idioms". Highly recommended!

Edit: As pointed out below by Neil, this book is quite out of date with many of his recommendations actually being incorporated into the C++ standard itself. However, I still find it to be a source of useful info, esp. in the form of his PLoP paper on C++ idioms where many idioms were recast into patterm form.


I always nitpick and edit the following:

  • Superfluous newlines
  • No newline at EOF

I usually stick to KNF described in *BSD STYLE(9)


I tend to put an else on all of my ifs.

if (condition)
{
    complicated code goes here
}
else
{
    /* This is a comment as to why the else path isn't significant */ 
}

Even though it annoys my coworkers. You can tell at a glance, that I considered the else case during coding.

참고URL : https://stackoverflow.com/questions/276173/what-are-your-favorite-c-coding-style-idioms

반응형