C++/가이드라인

덤프버전 :

파일:나무위키+넘겨주기.png   관련 문서: C++/문법

파일:나무위키+상위문서.png   상위 문서: C++



1. 개요
1.1. main 소스 파일
2. include
2.1. 전통적인 구조
2.1.1. 작성 방법
2.1.1.1. 선언부
2.1.1.2. 구현부
2.1.1.3. 실행부
2.2. 이름공간을 사용한 구조
2.3. Inlining 정의
2.3.1. 처리기 & static
2.3.2. 익명 이름공간
3. import C++20
4. 메모리 할당
4.1. 정적 할당
4.2. 자동 할당
4.3. 동적 할당
4.3.1. C언어 방식



1. 개요[편집]


C++의 코드 작성 방법을 간단하게 설명하는 문서. 곧 C언어 문서에서 연계되는 내용을 담고 있다. C++의 첫 시작을 어떻게 해야할지 보여주는 문서라고 할 수 있다. 이 문서는 헤더 구조를 벗어나 새롭게 C++ 코드를 작성하는 방법을 소개한다. 완고할 것 같았던 C++에도 변화의 바람이 불었으니, C++20에서 모듈의 도입으로 언어의 근본부터 엄청난 변화가 일어났기 때문에 전과 이후의 코드를 완전히 다른 방식으로 작성할 수 있다. 이전의 헤더를 사용하는 방식은 여전히 남아있으나, C언어의 방식 및 연결고리를 하나씩 대체해가고 있으며, 헤더도 과거의 유산이 될 예정이다. 그러나 C++20의 모듈은 현재 MSVC에서나 완전히 지원하고 나머지는 불완전하기 때문에 Windows 플랫폼에서만 온전히 사용할 수 있는 상태다. 이는 시간이 해답으로 보여진다.


1.1. main 소스 파일[편집]


[헤더 가져오기]

[객체 정의]

main 함수 정의


일반적으로 C++의 프로그램은 항상 진입점 함수
main
을 요구한다. 표준에 따르면 작성하는 방식은 두가지가 있다.
// 매개 변수가 없는 main 함수
int main()
{
    return 0;
}

// 매개 변수가 있는 main 함수
int main(int argc, const char** argv);
int main(const int& argc, const char* const* const& argv);

// 가능은 하지만 int main()을 쓰는 것이 권장된다
int main(int, const char**);
int main([[maybe_unused]] int argc, [[maybe_unused]] const char** argv);

main
함수는 C++ 프로그램의 시작과 끝을 담당한다. 프로그램의 시작 시에는
main
함수에 프로그램의 실행 매개변수를 전달할 수 있다[1]. 그리고 프로그램의 종료 시에는 정수 종료 코드를 반환한다. 종료 코드는 운영체제마다 다른 번호를 갖고 있는데, 보통
0
을 반환하면
main
함수의 실행에 오류가 없이 성공적으로 종료되었다는 뜻이다. 그러나
return 0;
은 반드시 해주지 않아도 된다. 아예 반환 구문이 없어도 컴파일러가 알아서 처리해준다.


2. include[편집]


C++은 C언어가 그랬듯 하나의 프로그램이 다수의 소스 코드가 모여 이루는 구조다. 원래 C++은 C언어의 구조를 그대로 가져왔기에 코드 구조도 같다. 반드시 정해진 건 아니지만, 보통 소스 코드를 선언(Declaration)구현(Implementation) 두가지로 나누어 작성하곤 한다. 선언부를 C언어에서는 헤더(Header, 머릿단)라고 불렀다. 으레 헤더는 프로그램에서 사용할 객체의 식별자 선언만 넣고, 구현 파일에서는 객체의 실제 정의를 넣는 방식이 널리 쓰인다. 그리고 헤더는 다른 헤더를 참조함으로써 그 헤더에 선언된 객체를 가져오고, 사용할 수 있다. 이렇게 하면 깔끔하고 컴파일 속도도 빠른 구조를 만들 수 있다. 만약 선언과 구현이 같은 파일에 정의되어 있다면 그걸 인라인(Inline)이다, 인라이닝 되어있다 등으로 칭한다.

여기서 가져온다는(Import) 행위는 헤더 파일을 삽입(
include
)함으로써 이루어진다. 삽입은 목표한 헤더 파일의 내용을 그대로 내 헤더로 복사하는 것이다. 정말 똑같이 복사하기 때문에 헤더파일과 헤더를 삽입하는 파일은 같은 파일로 합쳐지는 모양새가 된다.


2.1. 전통적인 구조[편집]


아직 많은 소스 코드는 이 규격을 따른다. 요약하자면 구현하는 데에 필요한 헤더를 먼저 가져오고, 그 아래에 클래스나 함수의 정의를 넣는 규칙이다.


2.1.1. 작성 방법[편집]



2.1.1.1. 선언부[편집]

MyPrinter.hpp
#include <string>
#include <string_view>

class MyPrinter
{
public:
    static std::string_view ClassName;

    MyPrinter(std::string_view contents) noexcept;

    void Print() const;

    [[nodiscard]] constexpr std::string& GetText() noexcept
    {
        return myText;
    }

    [[nodiscard]] constexpr const std::string& GetText() const noexcept;

private:
    std::string myText;
};

void Print(const MyPrinter& printer);

// 메서드는 인라이닝해도 문제 없음
constexpr const std::string& MyPrinter::GetText() const noexcept
{
    return myText;
}


[헤더 삽입]

객체 정의;

선언부에서는 객체를 선언한다. 필요한 헤더를
include
하고
private
,
public
,
protected
접근 권한에 따라서 클래스 내부에서 사용할 변수와 함수의 원형을 선언하면 된다. 편리하게도 메서드는 정적인 외부 연결 취급이므로
static
을 붙이지 않아도 구현 내용을 담을 수 있다. 정의 주의할 점은 이 헤더 파일에서
using namespace
를 사용했다가는, 헤더를
include
하는 모든 파일에도 같은 이름공간이 적용되어 예기치 않은 동작이 발생할 수 있으므로 주의해야 한다. 헤더 파일에서는 이름공간을 생략하지 않는 것이 좋다.

확장자의 경우
*.h
혹은
*.hpp
가 일반적이다.


2.1.1.2. 구현부[편집]

MyPrinter.cpp
#include "MyPrinter.hpp"
#include <iostream>
// <string>와 <string_view>도 삽입 되어있음

MyPrinter::MyPrinter(std::string_view contents) noexcept
    : myText(contents.cbegin(), contents.cend())
// C++20이상에서는 myText(contents)로 바로 문자열 생성이 가능함
{}

void MyPrinter::Print() const
{
    std::cout << myText << '\n';
}

void Print(const MyPrinter& printer)
{
    printer.Print();
}

using namespace std::literals;
std::string_view MyPrinter::ClassName = "MyPrinter"sv;


[원본 클래스 헤더 삽입]

[헤더 삽입]

객체 구현;

구현부에서는 필요한 외부 헤더를
include
한 다음에 헤더에서 선언한 객체의 내용을 실제로 구현한다.

확장자의 경우 비주얼 스튜디오에서는 C언어 및 C++ 소스코드에
*.cpp
확장자를 기본으로, G++에서는
*.cc
확장자를 기본으로 사용한다. 애초에 확장자는 프로그래머가 구별하기 쉽게 하기 위함이지 딱히 표준은 없다.


2.1.1.3. 실행부[편집]

Main.cpp
#include "MyPrinter.hpp"

int main()
{
    MyPrinter hangeul_printer{ "나무위키" };
    MyPrinter english_printer{ "NamuWiki" };

    hangeul_printer.Print();
    english_printer.Print();
}

실행부에는 필요한 헤더를
include
하고
main
함수를 작성한다.
main
함수의 소스에 클래스를 연동시키려면 클래스 선언 헤더를
include
해주면 된다. 참고로 IDE를 쓰고 있다면 해당은 없지만, 별도의 컴파일러를 쓴다면 구현 파일 또한 컴파일러에게 잘 전달해 주어야 링크 오류가 발생하지 않는다.


2.2. 이름공간을 사용한 구조[편집]


#include <string>
#include <string_view>

class MyPrinter
{
public:
    MyPrinter(std::string_view contents) noexcept;

    void Print() const;

protected:
    std::string text;
}

namespace namuwiki::guideline
{
    class HanguelPrinter : public MyPrinter
    {
    private:
        std::string text = "안내서";
    };

    class EnglishPrinter : public MyPrinter
    {
    private:
        std::string text = "Guideline";
    };
}

namespace namuwiki
{
    class HanguelPrinter : public MyPrinter
    {
    private:
        std::string text = "나무위키";
    };

    class EnglishPrinter : public MyPrinter
    {
    private:
        std::string text = "NamuWiki";
    };
}

C++의 이름공간을 활용하면 식별자의 중복 문제를 간단히 해결할 수 있다. 이름공간 안의 객체는 외부 연결로 취급되므로 선언만 존재해도 다른 코드에서 사용할 수 있다.


2.3. Inlining 정의[편집]


#include <string>

namespace IsThisOk
{
    class MyPrinter
    {
    public:
        void Print() const;
        void Print(const std::string& prefix) const;

    private:
        std::string text;
    };

    // 중복 정의된 함수 
    void Print(const MyPrinter& printer)
    {
        printer.Print(); // 식별자 찾지 못함
    }

    // 중복 정의된 함수 (오버로딩 때문이 아님!!!!)
    void Print(const MyPrinter& printer, const std::string& prefix)
    {
        printer.Print(prefix); // 식별자 찾지 못함
    }
}

여기서
IsThisOk::MyPrinter
클래스의
Print
메서드를 참조하는
Print
함수를 구현한다고 해보자. 그러면 두가지 방법이 있다. 다른 소스 파일에 구현하거나, 같은 헤더 파일에 구현하는 방법이 있다. 다른 소스 파일로 분리하면 아무 문제가 없다. 그런데 파일 접근 권한이 없어서 파일 생성을 못하고 수정 뿐이 못한다면? 아니면 소스를 외부에 배포하고 싶은데 간편한 사용을 위해 같은 헤더 파일에 구현하려고 한다고 해보자. 헌데 이렇게 하면 필시 링크 오류가 발생한다.

더욱이 가관인 건 오류가 두 곳에서 발생한다는 점이다. 예시를 한번 보자. 첫번째는
Print
함수가 중복 정의되었다는 오류다. 심지어 이 오류는 헤더 파일을 하나의 소스 파일에서만 써도 발생한다. 정확히 말하자면 헤더의 삽입은 파일을 병합하는 것이므로 같은 스코프에 정의된 객체는 그 내용이 인라인이면 식별자의 중복 문제가 발생한다. 두번째는
IsThisOk::MyPrinter::Print
식별자가 정의되지 않았다는 오류다. 이 오류는 식별자의 선언과 구현이 엇갈려서 생기는 문제다.
Print
는 오버로딩까지 된 함수라서 분명히 보이지 않는
extern "C++"
도 붙어있을 것이다. 그러나 여전히 링크 오류가 발생한다. 어째서일까?
헤더를 삽입하는 건 헤더의 내용을 그대로 복사하는 것임을 항상 유의해야 한다. 그렇기 때문에 헤더를 여러 곳에서 사용하면 소스 연결(Linking) 작업이 실패한다. 한 스코프에서 식별자는 항상 유일해야 하기 때문이다. 자세히는 헤더에 정의된 객체가 여러 obj 파일에서 참조되면 그걸 하나의 식별자로 보는 게 아니라 식별자의 이름이 중복으로 정의되었다고 인식하기 때문이다. 객체의 선언만 있으면 링크 오류가 발생하지 않는다. C++에선 조금 개선되어서 함수, 함수의 매개 변수, 클래스(구조체)의 이름을 서로 구분할 수 있다. 그나마 함수는 오버로딩이 가능하지만, 여전히 전역 변수와 클래스는 인라이닝을 할 수 없다. 템플릿으로만 간접적으로 중복된 이름을 만들 수 있다.


2.3.1. 처리기 & static[편집]


#ifndef MYCLASS_H
#define MYCLASS_H

namespace IsThisOk
{
    class MyPrinter
    {
    public:
        void Print() const
        {
            ...;
        }

    private:
        std::string text;
    };

    // 아직도 오류 발생: 중복 정의된 함수
    /*static */ void Print(const MyPrinter& printer)
    {
        printer.Print();
    }
}
#endif

C언어에서는 이 문제를 해결하기 위해 전처리기 키워드와
#ifdef
,
#endif
를 조합하는 방법이 이용되어 왔다. 클래스는 문제가 없지만, 그러나 여전히 함수는 문제가 된다. 전처리기 키워드를 쓰더라도, 함수는 여전히 참조하는 클래스의 메서드, 다른 함수들의 구현부를 필요로 한다. 이를 해결하는 방법은 이름공간의 사용 및 전역 함수에
static
을 써서 정적인 내부 연결(Static Internal Linkage)로 만드는 방법이 있다. 이러면 함수와 함수에서 참조하는 객체가 유일한 식별자로 정해지고 중복 정의 문제가 해결된다.


2.3.2. 익명 이름공간[편집]


namespace IsThisOk
{
    namespace
    {
        class MyPrinter
        {
        public:
            // 경고: 내부 연결이지만 구현부가 없음
            void Print() const;

        private:
            std::string text;
        };

        void Print(const MyPrinter& printer)
        {
            // 정의가 없어도 사용하는 데 문제없음
            printer.Print();
        }
    }
}

namespace
{
    int aaaaa;
    int bbbbb = 0;
}

int aaaaa; // 오류!

익명 이름공간의 멤버는 모두 내부 연결로 처리된다. 즉 익명 이름공간 안의 변수는
static
은 아닌데도 유일한 존재가 되며 구현부가 없는 함수도 멀쩡하게 사용할 수 있다.


3. import C++20 [편집]


종래의 헤더 삽입(Include)은 사실 소스 코드를 가져온다(Import)는 개념을 포함하긴 하는데 여러가지 부작용이 있었다. 대상 헤더의 원본을 그대로 내 헤더로 복사하는 것은 간편한 구현이지만 이후 프로그래밍의 발전사와는 동떨어진 개념이 되어버렸다. 과한 식별자 추가 및 의존성 문제, 보안 문제, 문자열로 관리되는 특성 상 유연한 헤더 변경과 삽입이 어려웠다. C++20은 여기에 모듈(Module)이라는 새로운 소스 파일 규격을 도입했다.


4. 메모리 할당[편집]



4.1. 정적 할당[편집]


코드 내의 정적인 크기를 가진 모든 변수들이며 자동 할당과 동적 할당과 달리 바이너리가 시작되면서 고정된 메모리 공간에 할당된다. 전역 변수와 함수 심볼 테이블이 그 예이며 프로그램이 종료될 때 까지 메모리에서 제거할 수 없다.
#include <array>

int myInt = 0;
char myChar;
char myCharArray[512];
std::array<int, 128> myIntArray;

int main()
{
    // ...
}



4.2. 자동 할당[편집]


함수/메소드 안에서 값/객체를 생성하면 일반적으로 자동 메모리 할당이다. 예를 들면
#include <iostream>

using namespace std;

int main() {
    int x = 0;

    cout << x << endl; //0

    return 0;
}

에서 "
int x = 0
"가 컴퓨터 메모리를 정수에 할당하는것이다. 자동 할당은 컴퓨터의 메모리중 스택이라는 곳에 저장된다. 이렇게 만들어진 자료는 함수의 범위가 끝나면 할당된 메모리가 회수된다. 따라서 다음같은 코드는 C++에선 먹히지 않는다.

int* zero_ptr() {
    int x = 0;
    int *ptr = &x; // Dangling pointer
    return ptr; //에러
}

이 코드의 의도는 '0'을 가리키는 포인터를 반환시키는 함수를 만드는것이다. 일단 자동 할당으로
x
를 생성하고, 정수 포인터인
ptr
에게
x
의 메모리 주소를 가리키라고 명령하고
ptr
를 반환하는것. 하지만 위에서 서술했듯이,
x
는 자동 할당으로 만들어진 자료기 때문에 {}로 정의된 함수의 범위 (scope)를 벗어나면
x
에게 주어진 메모리는 컴퓨터가 회수한다. 요약하자면 실제로 이 함수를 사용하면 나오는건 0을 가리키는 포인터가 아니라 아무 말도 안 되는 걸 가리키는, 사용할 수 없는 포인터다. 그렇다면 이걸 어떻게 고쳐야 할까? 답은 동적 메모리 할당 (dynamic memory allocation)이다.


4.3. 동적 할당[편집]


동적 할당은 스택 (stack) 대신 힙(heap)이란 메모리를 사용한다. 힙 영역에 메모리를 할당하려면 "
new
" 표현식을 사용하면 된다. 동적 메모리 할당을 하려면 "
T *ptr = new T
"같은 식으로 쓰자. 예제로, 위에서 의도한 함수를 만들려면 다음과 같이 코드를 짜면 된다.
int* zero_ptr() {
    int *ptr = new int; //동적 할당
    *ptr = 0;
    return ptr;
}


단 여기서 주의해야 할것이 있는데, 힙의 메모리를 자료에 할당해주면 이 자료는 써먹은 다음에 반드시 풀어주어야 한다. 그렇지 않으면 메모리 누수가 일어 나게 된다. "
new
"로 동적 할당한 메모리는 "
delete
" 지시자로 풀어주자.
#include <iostream>

using namespace std;

//.. zero_ptr() 함수를 전같이 정의..
int main() {
    int *ptr = zero_ptr(); //동적 할당

    cout << *ptr << endl; //0

    delete ptr; //ptr가 가리키는 메모리를 풀어줌. 이제 ptr은 재정의 되기 전까진 사용할 수 없다.

    return 0;
}

동적 메모리 할당은 자동/정적 할당보다 주의가 요구 된다. 정수 몇개 처럼 적은 메모리 용량을 차지하는 자료라면 별 상관 없겠지만, 좀 더 방대한 자료를 다루는 프로그램이라면 필요없는 자료를 풀어주지 않았다가 맛이 가버리는 수가 있다. 반대로 이미 해제한 메모리를 재차 해제하려 하거나(Double-Free), 이미 해제한 메모리에 접근하려 할 경우(Use-After-Free)에도 문제가 발생한다.

또한 서로 다른 C++ 런타임을 사용하는 코드가 있는 경우 해제(
delete
)는 할당(
new
)한 프로그램이 사용하는 런타임 영역 내에서 이루어 져야 한다.

GCC가 사용하는 libstdc++와 llvm이 사용하는 libc++, 그리고 MSVC가 사용하는 msvcp는 C 런타임과 다르게 서로 다른 ABI를 가지고 있으며 서로 할당 방법이 다르다. 예를 들어 libstdc++에서 할당한 포인터를 msvcp가 해제하는 경우 힙 커럽션등의 문제가 생길 수 있다.

마찬가지로 얼로케이터를 사용한 할당이나 아래의 libc 스타일의 malloc으로 할당한 포인터 또한 C++의
delete
로 해제해서는 안 된다. 그 반대도 마찬가지.

placement new
를 이용하여 이미 할당 된 메모리(스택도 포함)에 메모리를 할당 할 수도 있다 해당 방법으로 스택 메모리에다가 할당한 경우, 따로 해제를 해 주지 않아도 된다(다만, 클래스라면 소멸자는 호출 해 주어야 한다).

위와 같은 문제를 방지하기 위해 Modern C++(C++11 이상)에서는 소유권을 갖는 raw 포인터의 사용을 자제하도록 권고된다. 소유권 전달 시에는 스마트 포인터를 사용하자. 스마트 포인터의 특징은 참조 카운터를 사용하기 때문에 해당 포인터를 잡고 있는 모든 범위에서 벗어나면 스스로 해제된다는 점이다.
#include <memory> // weak_ptr, shared_ptr, unique_ptr, -- auto_ptr은 Deprecated
...
std::shared_ptr<int[]> ptr(new int[128]); // std::array<int, 128> ptr()...
...

다만 다차원 어레이는 C++17 이후에서만 스스로 해제가 가능하므로 사용한 자료형이나 클래스에 따라 람다식이나 별도의 템플릿을 작성해서 풀어줘야 누수가 생기지 않는다.


4.3.1. C언어 방식[편집]


파일:나무위키상세내용.png   자세한 내용은 C언어/문법 문서를 참고하십시오.

C언어/문법#동적 할당 문서의 동적 할당 문단을 참조하면 된다.

[1] 예를 들어서 Windows에서는 프로그램의 바로가기를 만들고 프로그램 경로 뒤에
-path "C:\\Program Files\\Microsoft"
처럼 매개변수를 전달할 수 있다


파일:CC-white.svg 이 문서의 내용 중 전체 또는 일부는
문서의 r331 판{{{#!wiki style="display: inline; display: 9;"
, 9번 문단}}}에서 가져왔습니다. 이전 역사 보러 가기
파일:CC-white.svg 이 문서의 내용 중 전체 또는 일부는 다른 문서에서 가져왔습니다.
[ 펼치기 · 접기 ]
문서의 r331 판{{{#!wiki style="display: inline; display: 9;"
, 9번 문단}}} (이전 역사)
문서의 r331 판{{{#!wiki style="display: inline; display: 10;"
, 10번 문단}}} (이전 역사)

문서의 r331 판{{{#!wiki style="display: inline; display: 11;"
, 11번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)




파일:크리에이티브 커먼즈 라이선스__CC.png 이 문서의 내용 중 전체 또는 일부는 2023-10-21 19:50:23에 나무위키 C++/가이드라인 문서에서 가져왔습니다.