(qt) c++ 소스를 공부하던중 extern 을 사용한 부분이 궁금해서 질문 드립니다.

pogusm의 이미지

http://www.ipcom.at/en/telephony/qjsimple/ 에서 QjSimple-0.6.6-src.zip 소스를 다운받아서
소스 분석도 해보고, 빌드도 해보고 하는 중입니다.
(인터넷전화 및 인스턴트메신저 클라이언트 프로그램 소스입니다. (QT)C++)

아래는 소스코드중 일부를 축약한 것입니다. (소스코드 내에 한글 주석으로 질문을 붙였습니다)

PjCallback *globalPjCallback;
 
PjCallback::PjCallback() {
	globalPjCallback = this;
}

PjCallback.cpp

#include "PjCallback.h"
 
class QjSimple : public QWidget {
public:
	QjSimple(QWidget *parent = 0);
	~QjSimple();
private:
	PjCallback *pjCallback;  // 1 - 멤버 변수가 선언되어 있는데
 
};

qjsimple.h

extern void *globalPjCallback;  // 2 - 왜 extern 을 이용해서 globalPjCallback 변수를 선언해서
 
QjSimple::QjSimple(QWidget *parent) :
	QWidget(parent) {
 
	globalPjCallback = pjCallback = 0;  // 3 - 이런식으로 정의하여
 
	pjCallback = new PjCallback();  // 4 - 이렇게 같이 사용하는 걸까요?
	QObject::connect((PjCallback*)globalPjCallback, SIGNAL(new_log_message(QString)),
			this, SLOT(dump_log_message(QString)), Qt::QueuedConnection);
}

qjsimple.cpp

위와같이 소스코드를 작성해서 얻는 이득이 무엇이기에 저렇게 한걸까요?

그냥 아래와 같이 수정해도 문제없이 실행되는거 같은데 말입니다.

	pjCallback = new PjCallback();
	QObject::connect(pjCallback , SIGNAL(new_log_message(QString)),
			this, SLOT(dump_log_message(QString)), Qt::QueuedConnection);

extern을 사용한 이유가 궁금합니다.

감사합니다.

HDNua의 이미지

객체가 한 프로그램에서 두 개 이상 생성될 필요가 없는 경우라면,
저런 식으로 외부에만 객체 포인터 하나를 설정해주고 다른 부분에서 초기화하여
객체를 다시 생성할 필요 없이 사용할 수 있으므로 시간이 절약됩니다.

Cocos2d를 이용해 iPhone 게임 만드는 걸 공부하는 중인데, CCDirector라는 클래스가 sharedDirector라는 클래스 메서드를 갖고 있어서
CCDirector를 새롭게 할당할 필요 없이 이 메서드만 사용하면 되더군요. 그와 비슷한 방식 아닐까 합니다.
제 생각은 이렇습니다.

저는 이렇게 생각했습니다.

pogusm의 이미지

"객체가 한 프로그램에서 두 개 이상 생성될 필요가 없는 경우" 라는 이야기를 어디서 들었던것도 같습니다.

아직 이해가 다 되지는 않지만... 좋은 답변 감사합니다.

klara의 이미지

같은 객체를 공유하기 위해서 입니다. extern으로 변수를 선언하면 새롭게 메모리를 할당하는게 아니라 링크단계에서 다른 오브젝트파일 (.o파일)에서 같은 이름으로 선언되어있는 변수를 끌어옵니다. '이렇게 해도 잘된다'고 하신 건 새롭게 객체를 할당하고 있기 때문에 서로다른 두개의 객체를 만드는 방법입니다. 만약 PjCallback 이 한번만 생성된다면, 결국 동일한 결과가 될 겁니다. 왜냐면 PjCallback의 생성자에서 글로벌 객체를 초기화 시키고 있기 때문입니다. 이경우는 pjCallback == globalPjCallback 입니다. 만약 여러번 생성한다면 마지막으로 생성한 녀석이 글로벌 객체가 됩니다.

pogusm의 이미지

globalPjCallback = pjCallback = 0;
pjCallback = new PjCallback(); // 첫번째 생성
pjCallback = new PjCallback(); // 두번째 생성 - globalPjCallback 이 가리키는 것.

위처럼 PjCallback 이 2개이상 생성될때,

가장 마지막에 생성된 객체를 가리키는 변수의 용도로 globalPjCallback 를 사용 하는거였군요..

좋은 답변 감사합니다.

그런데
extern으로 변수를 선언하여, 링크단계에서 다른 오브젝트파일에서 같은 이름으로 선언되어있는 변수를 끌어와야만 하는 이유는 아직 잘 모르겠네요..

class QjSimple : public QWidget {
public:
	QjSimple(QWidget *parent = 0);
	~QjSimple();
private:
	PjCallback *globalPjCallback;
	PjCallback *pjCallback;
};
 
 
QjSimple::QjSimple(QWidget *parent) :
	QWidget(parent) {
 
	globalPjCallback = pjCallback = 0;
 
	pjCallback = new PjCallback();
	QObject::connect(globalPjCallback, SIGNAL(new_log_message(QString)),
			this, SLOT(dump_log_message(QString)), Qt::QueuedConnection);
}

이렇게 해도 되는거 아닌지... 헷갈리네요 ^^;

klara의 이미지

extern은 서로 다른 오브젝트 파일간에 객체를 공유하기 위한 것입니다.
하나의 파일안에서 이렇게 하면 되지 않느냐는 거는 의미가 없습니다.
예를 들어 프로그램 전체에서 설정사항을 하나의 객체로 관리한다면 다음과 같이 되겠죠.

// pref.hpp
 
struct Pref {
    int config_a; double config_b;
    void load() { ... }
    void save() { ... }
};
 
// object_a.cpp
 
extern Pref *pref;
 
void ObjectA::doConfig() {if (pref) apply(pref->config_a);}
void ObjectA::setConfig(int config) {if (pref) pref->config_a = config;}
 
// object_b.cpp
 
extern Pref *pref;
 
void ObjectB::doConfig() {if (pref) apply(pref->config_b);}
void ObjectB::setConfig(double config) {if (pref) pref->config_b = config;}
 
// main.cpp
 
#include "pref.hpp"
#include "object_a.hpp"
#include "object_b.hpp"
 
Pref *pref = 0;
 
int main() {
    pref = new Pref;
    pref->load();
 
    ObjectA a;
    a.doConfig();
 
    ObjectB b;
    b.doConfig();
 
    a.setConfig(-1);
    b.setConfig(3.14);
 
    pref->save();
    delete pref;
    return 0;
}

이렇게 구현하는 것이 꼭 좋다는 게 아니라, 이런식으로 쓰일 수 있다는 예이고 그냥 지금 막 떠오르는대로 쓴거라 그다지 좋은 예제가 아닐 수도 있습니다.
암튼 이렇게 해서 컴파일 하면 object_a.o object_b.o main.o 세개의 오브젝트 파일이 나올 텐데, extern 선언이 없으면 각각의 오브젝트에 따로 변수가 선언된 셈이 됩니다.
하지만 지금은 object_a.o의 pref도, object_b.o의 pref도 모두 같은 하나의 Pref에 대한 인스턴스(main.o에서 생성한 인스턴스)를 가리켜야 하므로 extern으로 공유하는 것입니다.

extern 으로 가져오기 보다는, 오브젝트를 생성할 때 Pref에 대한 포인터를 넘겨주는 형식으로 처리할 수도 있을 겁니다.
혹은 객체의 유일성을 보장하기 위해서 싱글톤 패턴을 이용할 수도 있겠죠.

pogusm의 이미지

아주 잘 이해가 되었습니다.

"서로 다른 오브젝트 파일간에 객체를 공유"하기 위한 거였군요.

감사합니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.