이 페이지에서

Meta-Object Compiler (moc) 사용

Meta-Object Compiler(moc, moc)는 Qt의 C++ 확장을 처리하는 프로그램입니다.

moc 도구는 C++ 헤더 파일을 읽습니다. Q_OBJECT 매크로가 포함된 하나 이상의 클래스 선언을 발견하면, 해당 클래스에 대한 메타 객체 코드가 포함된 C++ 소스 파일을 생성합니다. 메타 객체 코드는 특히 시그널 및 슬롯 메커니즘, 런타임 유형 정보, 동적 속성 시스템 등에 필요합니다.

moc 가 생성한 C++ 소스 파일은 컴파일되어야 하며, 해당 클래스의 구현체와 링크되어야 합니다.

qmake와 CMake 모두 moc 를 적절히 호출하는 빌드 규칙이 포함된 makefile을 생성하므로, moc 를 직접 사용할 필요는 없습니다. qmake 는 기본적으로 이러한 빌드 규칙을 추가하는 반면, CMake의 경우 AUTOMOC 속성을 사용하여 moc 를 자동으로 처리할 수 있습니다. moc 에 대한 자세한 배경 정보는 “Qt는 왜 시그널과 슬롯에 Moc를 사용하나요?”를 참조하십시오 .

사용법

moc 일반적으로 다음과 같이 클래스 선언이 포함된 입력 파일과 함께 사용됩니다:

class MyClass : public QObject
{
    Q_OBJECT

public:
    MyClass(QObject *parent = 0);
    ~MyClass();

signals:
    void mySignal();

public slots:
    void mySlot();
};

위에 표시된 시그널과 슬롯 외에도, moc 는 다음 예제와 같이 객체 속성도 구현합니다. Q_PROPERTY() 매크로는 객체 속성을 선언하는 반면, Q_ENUM()는 속성 시스템 내에서 사용할 수 있도록 클래스 내의 열거형 목록을 선언합니다.

다음 예제에서는 Priority 열거형 타입의 속성을 선언합니다. 이 속성의 이름도 priority 이며, get 함수 priority() 와 set 함수 setPriority() 를 갖습니다.

class MyClass : public QObject
{
    Q_OBJECT
    Q_PROPERTY(Priority priority READ priority WRITE setPriority)

public:
    enum Priority { High, Low, VeryHigh, VeryLow };
    Q_ENUM(Priority)

    MyClass(QObject *parent = 0);
    ~MyClass();

    void setPriority(Priority priority) { m_priority = priority; }
    Priority priority() const { return m_priority; }

private:
    Priority m_priority;
};

Q_FLAG() 매크로는 플래그로 사용될, 즉 OR 연산으로 결합될 열거형을 선언합니다. 또 다른 매크로인 Q_CLASSINFO()를 사용하면 클래스의 메타 객체에 추가적인 이름/값 쌍을 연결할 수 있습니다:

class MyClass : public QObject
{
    Q_OBJECT
    Q_CLASSINFO("Author", "Oscar Peterson")
    Q_CLASSINFO("Status", "Active")

public:
    MyClass(QObject *parent = 0);
    ~MyClass();
};

moc 가 생성하는 출력은 프로그램 내의 다른 C++ 코드와 마찬가지로 컴파일 및 링크되어야 합니다. 그렇지 않으면 최종 링크 단계에서 빌드가 실패합니다. qmake 를 사용하는 경우, 이 작업은 자동으로 수행됩니다. qmake 가 실행될 때마다 프로젝트의 헤더 파일을 파싱하여, Q_OBJECT 매크로가 포함된 파일에 대해 moc 를 호출하는 make 규칙을 생성합니다. 마찬가지로, AUTOMOC를 ON 로 설정하면 CMake는 빌드 시점에 헤더 및 소스 파일을 스캔하고 그에 따라 moc 를 호출합니다.

myclass.h 파일에 클래스 선언이 발견되면, moc 출력은 moc_myclass.cpp 라는 파일에 저장되어야 합니다. 이 파일은 평소와 같이 컴파일되어야 하며, 그 결과 Windows에서는 moc_myclass.obj 와 같은 오브젝트 파일이 생성됩니다. 이 오브젝트 파일은 프로그램의 최종 빌드 단계에서 함께 링크될 오브젝트 파일 목록에 포함되어야 합니다.

호출을 위한 Make 규칙 작성 moc

가장 단순한 테스트 프로그램을 제외한 모든 경우, moc 의 실행을 자동화하는 것이 좋습니다. 프로그램의 makefile에 몇 가지 규칙을 추가하면, make 이 필요할 때 moc을 실행하고 moc 출력을 처리할 수 있습니다.

CMake나 qmake를 사용하여 필요한 모든 moc 처리를 수행하는 makefile을 생성할 수 있습니다.

직접 makefile을 작성하려는 경우, moc 처리를 포함하는 방법에 대한 몇 가지 팁을 소개합니다.

헤더 파일에 있는 Q_OBJECT 클래스 선언의 경우, GNU make만 사용하는 경우 다음 makefile 규칙이 유용합니다:

moc_%.cpp: %.h
        moc $(DEFINES) $(INCPATH) $< -o $@

이식성 있게 작성하려면 다음 형식의 개별 규칙을 사용할 수 있습니다:

moc_foo.cpp: foo.h
        moc $(DEFINES) $(INCPATH) $< -o $@

또한 ` SOURCES `(원하는 이름으로 대체) 변수에 ` moc_foo.cpp `을 추가하고, ` OBJECTS ` 변수에 ` moc_foo.o ` 또는 ` moc_foo.obj `을 추가해야 한다는 점을 기억해야 합니다.

두 예시 모두 $(DEFINES) 와 $(INCPATH) 가 C++ 컴파일러에 전달되는 정의 및 포함 경로 옵션으로 확장된다고 가정합니다. 이는 moc 가 소스 파일을 전처리하는 데 필요합니다.

C++ 소스 파일 이름은 .cpp 형식을 사용하는 것을 권장하지만, 원하신다면 .C, .cc, .CC, .cxx, .c++ 등과 같은 다른 확장자를 사용해도 됩니다.

Q_OBJECT 구현 파일(.cpp) 내의 클래스 선언에 대해서는 다음과 같은 makefile 규칙을 권장합니다:

foo.o: foo.moc

foo.moc: foo.cpp
        moc $(DEFINES) $(INCPATH) -i $< -o $@

이렇게 하면 make가 foo.cpp 을 컴파일하기 전에 moc을 실행하게 됩니다. 그런 다음

#include "foo.moc"

foo.cpp 파일의 끝부분에 추가할 수 있으며, 이 시점에서는 해당 파일에 선언된 모든 클래스가 완전히 파악된 상태입니다.

명령줄 옵션

다음은 moc에서 지원하는 명령줄 옵션입니다:

옵션설명
-D<macro>[=<def>]매크로를 정의하며, 정의 내용은 선택 사항입니다.
-E전처리만 수행하며, 메타 객체 코드는 생성하지 않습니다.
-f[<file>]출력에 #include 문을 강제로 생성합니다. 이 옵션은 확장자가 H 또는 h 로 시작하는 헤더 파일의 기본 설정입니다. 이 옵션은 표준 명명 규칙을 따르지 않는 헤더 파일이 있는 경우에 유용합니다. <file> 부분은 선택 사항입니다.
-FdirmacOS. 헤더 파일을 검색할 디렉터리 목록의 맨 앞에 프레임워크 디렉터리 dir 를 추가합니다. 이 디렉터리들은 -I 옵션으로 지정된 디렉터리들과 번갈아 배치되며, 왼쪽에서 오른쪽으로 순서대로 스캔됩니다(gcc 매뉴얼 페이지 참조). 일반적으로 -F /Library/Frameworks/를 사용합니다.
-h사용법과 옵션 목록을 표시합니다.
-i출력에 #include 문을 생성하지 않습니다. 이 옵션은 하나 이상의 클래스 선언이 포함된 C++ 파일에 대해 moc을 실행할 때 사용할 수 있습니다. 그런 다음 .cpp 파일에 있는 메타 객체 코드를 #include 해야 합니다.
-I<dir>헤더 파일을 위한 포함 경로에 dir을 추가합니다.
-M<key=value>플러그인에 추가 메타데이터를 추가합니다. 클래스에 Q_PLUGIN_METADATA 가 지정된 경우, 해당 키-값 쌍이 메타데이터에 추가됩니다. 이는 런타임에 플러그인을 위해 해결되는 Json 객체에 포함되며( QPluginLoader 에서 접근 가능), 이 인수는 일반적으로 빌드 시스템에서 해결된 정보로 정적 플러그인에 태그를 지정하는 데 사용됩니다.
-nw경고를 생성하지 않습니다. (권장하지 않음.)
-o<file>출력을 표준 출력이 아닌 <file> 에 기록합니다.
-p<path>moc가 생성된 #include 문에서 파일 이름 앞에 <path>/ 을 추가하도록 합니다.
-U<macro>매크로를 정의 해제합니다.
@<file><file> 에서 추가 명령줄 옵션을 읽습니다. 파일의 각 줄은 하나의 옵션으로 처리됩니다. 빈 줄은 무시됩니다. 이 옵션은 옵션 파일 자체 내에서 지원되지 않는다는 점에 유의하십시오(즉, 옵션 파일이 다른 파일을 "포함"할 수 없음).
-vmoc 의 버전 번호를 표시합니다.

헤더 파일의 특정 부분을 분석하지 않도록 moc에 명시적으로 지시할 수 있습니다. moc 은 전처리기 심볼 Q_MOC_RUN 을 정의합니다.

#ifndef Q_MOC_RUN
    ...
#endif

로 둘러싸인 모든 코드는 moc 에 의해 건너뜁니다.

진단

moc 명령어는 Q_OBJECT 의 클래스 선언에 포함된 여러 위험하거나 유효하지 않은 구문에 대해 경고를 표시합니다.

프로그램의 최종 빌드 단계에서 YourClass::className() 가 정의되지 않았거나 YourClass 에 vtable이 없다는 내용의 링크 오류가 발생한다면, 무언가 잘못된 것입니다. 대부분의 경우, moc으로 생성된 C++ 코드를 컴파일하거나 ` #include `를 실행하는 것을 잊었거나, (전자의 경우) 링크 명령에 해당 객체 파일을 포함시키는 것을 잊은 것입니다. ` qmake`를 사용하는 경우, 이 명령을 다시 실행하여 메이크파일을 업데이트해 보십시오. 그러면 문제가 해결될 것입니다.

빌드 시스템

헤더 moc 파일 포함

qmake와 CMake는 헤더 moc 파일을 포함하는 방식이 다릅니다.

예를 들어, a.h, a.cpp, b.h, b.cpp 와 같은 두 개의 헤더와 이에 대응하는 소스 파일이 있다고 가정해 보겠습니다. 각 헤더에는 Q_OBJECT 매크로가 있습니다:

// a.h
class A : public QObject
{
    Q_OBJECT

    public:
        // ...
};
// a.cpp
#include "a.h"

// ...

#include "moc_a.cpp"
// b.h
class B : public QObject
{
    Q_OBJECT

    public:
        // ...
};
// b.cpp
#include "b.h"

// ...

#include "moc_b.cpp"

qmake를 사용할 때, moc으로 생성된 파일(moc_a.cpp/moc_b.cpp)을 포함하지 않으면 a.cpp, b.cpp, moc_a.cpp 및 moc_b.cpp 가 각각 별도로 컴파일됩니다. 이로 인해 빌드 속도가 느려질 수 있습니다. moc으로 생성된 파일을 포함하면, moc으로 생성된 코드가 해당 파일들에 포함되어 있으므로 a.cpp와 b.cpp만 컴파일하면 됩니다.

CMake를 사용할 때, 해당 파일을 포함하지 않으면 moc에 의해 하나의 추가 파일이 생성됩니다(예시를 위해 이 파일을 cmake.cpp 라고 부르겠습니다). cmake.cpp 는 moc_a.cpp 와 moc_b.cpp 를 모두 포함합니다. CMake에서는 moc로 생성된 파일을 포함하는 것이 여전히 허용되지만, 반드시 필요한 것은 아닙니다.

이 주제와 관련된 CMake의 moc 지원에 대한 자세한 내용은 ‘소스 파일에 헤더 moc 파일 포함하기’를 참조하십시오.

제한 사항

moc 모든 C++ 기능을 처리하지는 않습니다. 주요 문제는 클래스 템플릿에 Q_OBJECT 매크로를 사용할 수 없다는 점입니다. 다음은 예시입니다:

class SomeTemplate<int> : public QFrame
{
    Q_OBJECT
    ...

signals:
    void mySignal(int);
};

다음과 같은 구문은 허용되지 않습니다. 이 모든 구문에는 대체 방법이 있으며, 대체 방법이 일반적으로 더 낫다고 판단되므로 이러한 제한 사항을 제거하는 것은 당사의 우선순위가 높지 않습니다.

다중 상속 시 QObject가 첫 번째여야 함

다중 상속을 사용하는 경우, ` moc `는 첫 번째 상속받은 클래스가 ` QObject`의 하위 클래스라고 가정합니다. 또한, 첫 번째 상속받은 클래스만이 ` QObject`이어야 합니다.

// correct
class SomeClass : public QObject, public OtherClass
{
    ...
};

QObject 을 사용한 가상 상속은 지원되지 않습니다.

함수 포인터는 시그널 또는 슬롯 매개변수로 사용할 수 없음

함수 포인터를 시그널이나 슬롯 매개변수로 사용하려는 대부분의 경우, 상속을 사용하는 것이 더 나은 대안이라고 생각합니다. 다음은 유효하지 않은 구문의 예입니다:

class SomeClass : public QObject
{
    Q_OBJECT

public slots:
    void apply(void (*apply)(List *, void *), char *); // WRONG
};

이 제한 사항은 다음과 같이 우회할 수 있습니다:

typedef void (*ApplyFunction)(List *, void *);

class SomeClass : public QObject
{
    Q_OBJECT

public slots:
    void apply(ApplyFunction, char *);
};

경우에 따라 함수 포인터를 상속과 가상 함수로 대체하는 것이 더 나을 수도 있습니다.

신호 및 슬롯 매개변수로 사용되는 열거형(Enum)과 타입 정의(Typedef)는 완전한 한정자(fully qualified)여야 합니다

QObject::connect()는 인자의 시그니처를 확인할 때 데이터 유형을 문자 그대로 비교합니다. 따라서 Alignment 와 Qt::Alignment 는 서로 다른 두 유형으로 간주됩니다. 이 제한을 우회하려면 시그널과 슬롯을 선언할 때, 그리고 연결을 설정할 때 데이터 유형을 완전히 명시해야 합니다. 예를 들면 다음과 같습니다:

class MyClass : public QObject
{
    Q_OBJECT

    enum Error {
        ConnectionRefused,
        RemoteHostClosed,
        UnknownError
    };

signals:
    void stateChanged(MyClass::Error error);
};

중첩된 클래스에는 시그널이나 슬롯을 가질 수 없습니다

다음은 문제가 되는 구문의 예입니다:

class A
{
public:
    class B
    {
        Q_OBJECT

    public slots:   // WRONG
        void b();
    };
};

신호/슬롯의 반환 유형은 참조일 수 없음

시그널과 슬롯은 반환형을 가질 수 있지만, 참조를 반환하는 시그널이나 슬롯은 void를 반환하는 것으로 간주됩니다.

클래스의 ` signals ` 및 ` slots ` 섹션에는 신호와 슬롯만 포함될 수 있습니다

moc 클래스의 signals 또는 slots 섹션에 시그널과 슬롯 이외의 다른 구문을 넣으려고 하면 오류가 발생합니다.

'메타 객체 시스템', '신호 및 슬롯', 'Qt의 속성 시스템'항목도 참조하십시오 .

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.