https://herbsutter.com/2013/05/24/gotw-6a-const-correctness-part-1-3/

https://herbsutter.com/2013/05/24/gotw-6b-const-correctness-part-2/

https://herbsutter.com/2013/05/28/gotw-6b-solution-const-correctness-part-2/


* 번역이 아니라 이해하고 정리한 내용입니다. 따라서 원 글의 의도가 전달되지 않을 수 있으며, 수정 될 수 있습니다.


[ ] https://herbsutter.com/2013/05/24/gotw-6a-const-correctness-part-1-3/


1. shared variable

Shared variable이란 하나 이상의 thread에서 동시에 접근할 수 있는 변수를 말한다.


2. shared variable의 관점에서 const 나 mutable이란?

const member function은 다음 두가지 중 하나를 반드시 만족해야 한다. (절대 공유되지 않는다는 가정이 있지 않는 한)

 - 진짜로 const(truly physically/bitwise const). 다시 말해 객체의 어떤 데이터에도 write하지 않아야 한다.

 - 내부적으로 동기화되어서 (예를 들어 mutex, atomic<> 등으로) 동시에 호출되더라도 문제가 없어야 한다.


비슷하게, 멤버 변수에 mutable 키워드를 쓴다는 것은 그 변수가 쓸 쑤 있지만 논리적으로는 const(writable but logically const) 임을 의미한다.

- 논리적으로 const란 여러 동시성 (const) 연산들로부터 안전하다는 의미이다.

- mutable이 붙어있다는 것은 어떤 const operation이 사실은 write할지 모른다는 뜻이고, 동시성을 갖고 읽고 쓰는데 문제가 없다는 의미이다. 따라서 mutex 나 유사한 동기화 로직 아니면 atomic<>으로 만들어야 한다.


따라서, mutex 와 mutable 을 같이 써야 한다. (또는 atomic<> 과 mutable)


3. c++98과 c++11에서 mutable과 const는 뭐가 다른가?


먼저 그전에...

이 내용을 정리하기 전에, const와 mutable에 대해 헷갈리게 했던 부분은 const_cast를 통해 수정하면 되지 왜 mutable을 써야 하지? 하는 점이었다.

예를 들면 아래 처럼...


#include <vector>

class Tile { ... };

class TileStore {
public:
Tile* getTile() const {
if (empty_tiles_.empty()) {
const_cast<TileStore*>(this)->ReserveMoreTiles();
}
// ...
return //something;
 }
void ReserveMoreTiles() {
// ...
}
private:
std::vector<Tile*> empty_tiles_;
};


먼저 아래 링크을 가져왔다.

http://stackoverflow.com/questions/11457953/const-cast-vs-mutable-any-difference


const_cast는 사실 대상 객체의 const 속성을 제거하는 것이 아니다. 대신 접근 경로(access path)상에 const 속성을 바꿔주는 것이다.

따라서 진짜 const 객체에 대하여 const_cast를 수행하는 것은 Undefined behavior를 야기할 수 있다.


  const int j = 5; // constant object
  const int *p = &j; // `p` is a const access path to `j`

  int *q = const_cast<int *>(p); // `q` is a non-const access path to `j`
  *q = 10; // UNDEFINED BEHAVIOR !!!


그럼 mutable은 뭘까? [1]

 - mutable로 지정된 멤버는 외부에 노출된 클래스의 상태(the externally visible state of the class)에 영향을 받지 않는다.

 - const 클래스의 mutable 멤버는 수정 가능하다.

 - c++은 mutable을 storage-class-specifier로 취급한다.


mutable이 단순히 상수성을 제거한다는 생각을 했는데, 중요한 포인트중 하나는 class가 const여도 된다는 것이다.

즉,

* const_cast는 애초에 다루는 객체가 const이면 안된다.

* mutable은 다루는 객체가 const여도 modifable하다.


위에 적당히 만들어본 Tile & TileStore를 아래처럼 고쳐보았다.


#include <vector>
#include <stdio.h>

class Tile { public: int i; };
class TileStore { public: Tile* getTile() const { if (empty_tiles_.empty()) { const_cast<TileStore*>(this)->ReserveMoreTiles(); }
return empty_tiles_[0]; } void ReserveMoreTiles() {
Tile* tile = new Tile(); tile->i = 10; empty_tiles_.push_back(tile); } private: std::vector<Tile*> empty_tiles_; };
int main() { const TileStore a; Tile* t = a.getTile(); printf("%d\n", t->i); return 0; }

이 동작은 undefined behavior이다. const 객체의 constness를 제거하는 로직이니까.

mutable로 empty_tiles_를 선언해야 맞는 것 같다.


결론적으로 const_cast는 위험하다! (const 객체 안쓴다고 확신할 수 있는가?)


PS. 하지만, QT 5.6으로 컴파일뿐 아니라 실행은 잘 된다. (undefined.... != crash)


3으로 다시 돌아가서....


C++98 single threaded code는 문제 없다. ( 왜 c++98 single threaded code라고 부르는지 모르겠다. )

C++98에서 우리는 const는 논리적인 const(logically const)이며, 물리적인/비트레벨의 const(phisically/bitwise)는 아니다라고 가르쳐왔다.


이제 C++11에서는 const란 read-only 또는 동시성을 고려해서 안전하게 읽을수 있음(safe to read concurrently)을 보장한다.(해야 한다.)

다시 말해 물리적인/비트레벨의 const이거나 내부적으로 동기화되어 어떠한 쓰기 동작도 concurrent한 read access들로부터 동기화되어야 한다.


요약해서,

const correctness 코드를 짜라.

const를 올바르게 사용하는 것은 올바르게 동기화된 코드를 위해 필요하다. 또 const는 그 자체로 "값을 수정하지 않겠다는" 훌륭한 문서이고 컴파일러가 더 나은 코드를 만드는 것을 도와준다.


cosnt 객체나 동작(메소드)에서 멤버를 수정해야 한다면 mutable을 mutex 또는 atomic과 함께 써라.

모든 상용 라이브러리들이 const correct하진 않다.


[ ] https://herbsutter.com/2013/05/28/gotw-6b-solution-const-correctness-part-2/


1. 함수를 선언할때 레퍼런스가 아닌 파라미터의 const는 의미 없다.


void a(const int);
void a(int); // same

void b(const int&);
void b(int&); // different

그러나 inline 함수는 선언과 정의가 같이 있으니까 적는게 의미가 있다.


2. 이항 연산자의 overload시 rhs는 const, lhs는 const가 아닌것이 좋다.

// 잘못된 예제 코드
polygon operator+( polygon& lhs, polygon& rhs ) {
auto ret = lhs;
auto last = rhs.get_num_points();
for( auto i = 0; i < last; ++i ) // concatenate
ret.add_point( rhs.get_point(i) );
return ret;
}

lhs를 ret에 복사하고 있다.

결국 내부에서 한번의 객체 복사가 이뤄져야 하는데, 이 객체가 앞서 호출된 임시객체인 것이 유리할 수 있다.

예를 들어 x = a + b + c;

a + b를 수행하면서 생긴 ret객체를 다시 lhs로 받아서 c와 더하게 된다.


즉, 아래와 같이 하는 것이 비용이 적을 가능성이 있다.

polygon operator+(polygon lhs, const polygon& rhs) {
// ...
return lhs;
}


참조

[1] http://en.cppreference.com/w/cpp/language/cv

저작자 표시
신고

'프로그래밍 언어 > C/C++' 카테고리의 다른 글

[읽은 글] const correctness  (0) 2016.10.18
[C++11] std::function의 성능  (0) 2015.05.29
GCC options  (0) 2013.12.05
[C++11] Range based for loop  (4) 2013.11.13
[C++11] Move semantics  (4) 2013.11.10
[c++11] rvalue reference  (1) 2013.11.07
Posted by 소혼

[media query 간략 소개]

Media query는 media의 종류에 따라 특정 CSS 스타일을 적용하고자 할 때  사용하는 CSS 스펙 가운데 하나입니다.

(http://www.w3.org/TR/css3-mediaqueries/)



media query에 대한 article : http://html.nhncorp.com/blog/42284


다음글 : Media query in webkit (style element)

그 다음글 : Media query in webkit(import/media rule)







예를 들어 아래는 대표적인 media query의 예입니다. (이 글에서 코드리딩할 방법입니다.)

### html
<link rel="stylesheet" type="text/css" media="screen" href="sans-serif.css">
<link rel="stylesheet" type="text/css" media="print" href="serif.css">

screenprint는 가장 많이 알려진 미디어의 종류(type)입니다.


또는 아래처럼 쓸 수도 있습니다.

### css
@media screen {
 * { font-family: sans-serif }
}


또 @import rule을 사용하여 아래와 같은 방법도 있습니다.

### css

@import url(example.css) screen


style element 자체에도 media query를 사용할 수 있습니다.

### html

<style media="screen and (min-width:300px)">
body { background-color:red; }
</style>


media query라는 단어가 말하듯 단순히 media의 종류를 지정하는 것 뿐 아니라 간단한 형태의 조건을 줄 수도 있습니다.

자세한 내용은 위에 링크한 스펙을 참고하시기 바랍니다.


간단히 정리하면 다음과 같습니다.

  • 여러 media type을 지정하고 싶다면 , 를 사용합니다. (screen, print)
  • media에 추가로 특정 media feature만 적용되도록 제한하고 싶다면 media feature와 media value로 이루어지는 수식들을 and로 추가할 수 있습니다.
  • only, not과 같은 제한자를 가질 수 있습니다.


예를 들면, 아래와 같습니다.

### css

<link rel="stylesheet" media="speech and (min-device-width: 800px)" href="example.css" />



[WebKit internal]

일단 위 예제 1의 <link>의 경우부터 시작하겠습니다. (1번라인을 기준으로 설명하겠습니다.)

네트워크로부터 데이터를 가져와서 link 태그를 파싱하면 HTMLLinkElement를 만들게 됩니다.

그 다음 Attribute들을 파싱하기 위해 parseAttribute를 부릅니다.


### c++; highlight: [9, 10]

void HTMLLinkElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
{
    if (name == relAttr) {
        m_relAttribute = LinkRelAttribute(value);
        process();
    } else if (name == hrefAttr) {
         // ...
    } else if (name == mediaAttr) {
        m_media = value.string().lower();
        process();
    } else if (name == disabledAttr)
         // ...
    else {
        if (name == titleAttr && m_sheet)
            m_sheet->setTitle(value);
        HTMLElement::parseAttribute(name, value);
    }
}

만약 현재 처리할 attribute가 media라면(9 line) value(screen)를 소문자로 변경하여 m_media에 넣고 process()를 호출합니다.


### c++; highlight:[10, 11, 12]

void HTMLLinkElement::process()
{
    // ...

    if (m_disabledState != Disabled && (m_relAttribute.m_isStyleSheet || (acceptIfTypeContainsTextCSS && type.contains("text/css")))
        && document()->frame() && m_url.isValid()) {

        // ...

        bool mediaQueryMatches = true;
        if (!m_media.isEmpty()) {
            RefPtr<RenderStyle> documentStyle = StyleResolver::styleForDocument(document());
            RefPtr<MediaQuerySet> media = MediaQuerySet::createAllowingDescriptionSyntax(m_media);
            MediaQueryEvaluator evaluator(document()->frame()->view()->mediaType(), document()->frame(), documentStyle.get());
            mediaQueryMatches = evaluator.eval(media.get());
        }

        // Don't hold up render tree construction and script execution on stylesheets
        // that are not needed for the rendering at the moment.
        bool blocking = mediaQueryMatches && !isAlternate();
        addPendingSheet(blocking ? Blocking : NonBlocking);

        // Load stylesheets that are not needed for the rendering immediately with low priority.
        ResourceLoadPriority priority = blocking ? ResourceLoadPriorityUnresolved : ResourceLoadPriorityVeryLow;
        CachedResourceRequest request(ResourceRequest(document()->completeURL(m_url)), charset, priority);
        request.setInitiator(this);
        m_cachedSheet = document()->cachedResourceLoader()->requestCSSStyleSheet(request);

        if (m_cachedSheet)
            m_cachedSheet->addClient(this);
        else {
            // The request may have been denied if (for example) the stylesheet is local and the document is remote.
            m_loading = false;
            removePendingSheet();
        }

       // ...

}

기본적으로 mediaQueryMatches는 true 입니다.

그러나, m_media가 있다면, FrameView의 mediaType, frame, documentStyle과 비교하여 mediaQuery가 match되는지 확인합니다.(9-12 line)

MediaQueryEvaluator가 이 값들을 가지고 있다가 eval을 통해 비교할 것입니다. (12 line)


만약 match된다면, loader에게 정상적으로 로드요청을 하겠지만, 아니면, blocking되었다가 제거될 것입니다.


m_media는 간단하게는 screen과 같이 media type만 있을 수도 있지만, "screen and (color:1), projection" 과 같이 여러 media type들과 media feature로 이루어진 수식을 포함할지도 모릅니다.


따라서 MediaQueryEvaluator가 쉽게 계산할 수 있도록 MediaQuerySet::createAllowingDescriptionSyntax을 이용하여 media query set으로 변환합니다.(10 line)


### c++ : highlight: [7, 9, 15]

bool MediaQuerySet::parse(const String& mediaString)
{
    CSSParser parser(CSSStrictMode);

    Vector<OwnPtr<MediaQuery> > result;
    Vector<String> list;
    mediaString.split(',', list);
    for (unsigned i = 0; i < list.size(); ++i) {
        String medium = list[i].stripWhiteSpace();
        if (medium.isEmpty()) {
            if (!m_fallbackToDescriptor)
                return false;
            continue;
        }
        OwnPtr<MediaQuery> mediaQuery = parser.parseMediaQuery(medium);
        if (!mediaQuery) {
            if (!m_fallbackToDescriptor)
                return false;
            String mediaDescriptor = parseMediaDescriptor(medium);
            if (mediaDescriptor.isNull())
                continue;
            mediaQuery = adoptPtr(new MediaQuery(MediaQuery::None, mediaDescriptor, nullptr));
        }
        result.append(mediaQuery.release());
    }
    // ",,,," falls straight through, but is not valid unless fallback
    if (!m_fallbackToDescriptor && list.isEmpty()) {
        String strippedMediaString = mediaString.stripWhiteSpace();
        if (!strippedMediaString.isEmpty())
            return false;
    }
    m_queries.swap(result);
    return true;
}

createAllowingDescriptionSyntax는 m_fallbackToDescriptor를 true로 하는 MediaQuerySet을 만들어 parse를 부르는게 전부입니다.

parse는 넘겨받은 mediaString을 ,로 구분해서(7 line) strip하고 (9 line) CSSParser에게 parsing을 요청하는게 전부입니다.(15 line)

MediaQuery는 세가지 주요 인자를 갖는데 Restrictor( Only, Not, None)와 mediaType(String), ExpressionVector입니다.


ExpressionVector 는 MediaQueryExp를 갖는 자료형으로 Exp가 만들어지면서 유효한 expression들이 vector에 들어가게 됩니다.


이제 MediaQuery는 만들어졌으니 이전 소스코드에서 MediaQueryEvaluator::eval을 보도록 하겠습니다.

### c++; highlight: [6, 18, 19, 24]

bool MediaQueryEvaluator::eval(const MediaQuerySet* querySet, StyleResolver* styleResolver) const
{
    if (!querySet)
        return true;

    const Vector<OwnPtr<MediaQuery> >& queries = querySet->queryVector();
    if (!queries.size())
        return true; // empty query list evaluates to true

    // iterate over queries, stop if any of them eval to true (OR semantics)
    bool result = false;
    for (size_t i = 0; i < queries.size() && !result; ++i) {
        MediaQuery* query = queries[i].get();

        if (query->ignored())
            continue;

        if (mediaTypeMatch(query->mediaType())) {
            const Vector<OwnPtr<MediaQueryExp> >* exps = query->expressions();
            // iterate through expressions, stop if any of them eval to false
            // (AND semantics)
            size_t j = 0;
            for (; j < exps->size(); ++j) {
                bool exprResult = eval(exps->at(j).get());
                if (styleResolver && exps->at(j)->isViewportDependent())
                    styleResolver->addViewportDependentMediaQueryResult(exps->at(j).get(), exprResult);
                if (!exprResult)
                    break;
            }

            // assume true if we are at the end of the list,
            // otherwise assume false
            result = applyRestrictor(query->restrictor(), exps->size() == j);
        } else
            result = applyRestrictor(query->restrictor(), false);
    }

    return result;
}

6 line에서 qureySet을 vector로 변환하여 loop를 돌리면서

먼저 type이 같은지 확인합니다.(18 line)


같다면 expression들을 꺼내 (19 line), 수식을 eval()합니다.

전 예제에서 styleResolver를 넘기지 않았기 때문에 addViewportDependentMediaQueryResult는 수행하지 않습니다.

* 현재 사용중인 예제는 media type만 갖고 있습니다. 만약에 max-width:300처럼 media expression이 있다면 어떻게 될까요?

   현재 width가 1024라고 가정하면, 해당 엘리먼트는 로드하지 않을 것입니다.

   그러나, 브라우저 크기를 줄여서 width가 300이하가 되면? 해당 스타일 시트를 로드해야 합니다.

   이러한 처리를 하기 위한 것이 바로 addViewportDependentMediaQueryResult입니다.

   이 부분의 동작은 다음 글과 다다음 글을 통해 따라가 보도록 하겠습니다.


### c++; highlight: [6, 20]

static void createFunctionMap()
{
    // Create the table.
    gFunctionMap = new FunctionMap;
#define ADD_TO_FUNCTIONMAP(name, str)  \
    gFunctionMap->set(name##MediaFeature.impl(), name##MediaFeatureEval);
    CSS_MEDIAQUERY_NAMES_FOR_EACH_MEDIAFEATURE(ADD_TO_FUNCTIONMAP);
#undef ADD_TO_FUNCTIONMAP
}

bool MediaQueryEvaluator::eval(const MediaQueryExp* expr) const
{
    if (!m_frame || !m_style)
        return m_expResult;

    if (!expr->isValid())
        return false;

    if (!gFunctionMap)
        createFunctionMap();

    // call the media feature evaluation function. Assume no prefix
    // and let trampoline functions override the prefix if prefix is
    // used
    EvalFunc func = gFunctionMap->get(expr->mediaFeature().impl());
    if (func)
        return func(expr->value(), m_style.get(), m_frame, NoPrefix);

    return false;
}

수식의 eval은 mediaFeature마다 정의된 함수를 수행하는 것입니다.

함수의 등록은 createFunctionMap안의 매크로로 이루어집니다.(20 line)


현재 등록된 mediaFeature는이 글 제일 아래를 참고하시기 바랍니다. 웹킷은 현재 CSS 스펙에 없는 media feature도 몇 가지 지원하고 있습니다.(예: device-pixel-ratio)


media feature마다 함수와 구현이 다 다르기 때문에 각각의 구현을 확인하시려면 Source/WebCore/css/MediaQueryEvaluator.cpp 를 참고하시기 바랍니다.


예제로 color를 보도록 하겠습니다. 6 line에서 보듯 'media feature명' + MediaFeatureEval()이라는 함수를 봐야 찾아야 합니다.


### c++; highlight: [3, 6]

static bool colorMediaFeatureEval(CSSValue* value, RenderStyle*, Frame* frame, MediaFeaturePrefix op)
{                                                                                      
    int bitsPerComponent = screenDepthPerComponent(frame->page()->mainFrame()->view());
    float number;                                                                      
    if (value)                                                                         
        return numberValue(value, number) && compareValue(bitsPerComponent, static_cast<int>(number), op);

    return bitsPerComponent != 0;                                                      

color 의 경우 screenDepthPerComponent라는 platform specific function으로부터 값을 꺼내오고 있습니다.해당 함수는 Source/WebCore/platform/xxx/PlatformScreenXXX.cpp에 각 포트별로 구현되어 있습니다.(XXX는 포트명)


value가 있다는 것은 현재 확인하고 있는 expression이 >, < 와 같은 비교문이 있다는 뜻(ex - color:256)입니다.

MediaFeaturePrefix는 비교하는 media feature앞에 min- 또느 max-가 붙어있는지를 전달합니다. 이 값에 따라 compareValue가 ==을 비교할지 <= 나 >=를 비교할지가 결정됩니다.





현재 등록된 media feature의 종류

### c++

#define CSS_MEDIAQUERY_NAMES_FOR_EACH_MEDIAFEATURE(macro) \
    macro(color, "color") \
    macro(grid, "grid") \
    macro(monochrome, "monochrome") \
    macro(height, "height") \
    macro(hover, "hover") \
    macro(width, "width") \
    macro(orientation, "orientation") \
    macro(aspect_ratio, "aspect-ratio") \
    macro(device_aspect_ratio, "device-aspect-ratio") \
    macro(device_pixel_ratio, "-webkit-device-pixel-ratio") \
    macro(device_height, "device-height") \
    macro(device_width, "device-width") \
    macro(max_color, "max-color") \
    macro(max_aspect_ratio, "max-aspect-ratio") \
    macro(max_device_aspect_ratio, "max-device-aspect-ratio") \
    macro(max_device_pixel_ratio, "-webkit-max-device-pixel-ratio") \
    macro(max_device_height, "max-device-height") \
    macro(max_device_width, "max-device-width") \
    macro(max_height, "max-height") \
    macro(max_monochrome, "max-monochrome") \
    macro(max_width, "max-width") \
    macro(max_resolution, "max-resolution") \
    macro(min_color, "min-color") \
    macro(min_aspect_ratio, "min-aspect-ratio") \
    macro(min_device_aspect_ratio, "min-device-aspect-ratio") \
    macro(min_device_pixel_ratio, "-webkit-min-device-pixel-ratio") \
    macro(min_device_height, "min-device-height") \
    macro(min_device_width, "min-device-width") \
    macro(min_height, "min-height") \
    macro(min_monochrome, "min-monochrome") \
    macro(min_width, "min-width") \
    macro(min_resolution, "min-resolution") \
    macro(pointer, "pointer") \
    macro(resolution, "resolution") \
    macro(transform_2d, "-webkit-transform-2d") \
    macro(transform_3d, "-webkit-transform-3d") \
    macro(transition, "-webkit-transition") \
    macro(animation, "-webkit-animation") \
    macro(view_mode, "-webkit-view-mode")



저작자 표시 비영리 변경 금지
신고
Posted by 소혼

virtual키워드를 사용해 child 객체의 메소드들을 호출할 수 있다는 것은 C++을 공부한 사람이라면 다들 알고 있는 사항이다.

예를 들어, 아래의 코드에서는 child의 call method가 호출된다.

### c++

class parent {

public: 

    virtual void call();

};

class child : public parent {

public:

    void call();

};

int main()

{

    parent* p = new child;

    p->call(); // child의 call이 호출됨

}



그렇다면 child의 instance는 virtual로 선언된 메소드들에 대해서 항상 child를 호출하는가?

오늘 그렇지 않다는 것을 배웠다.


아래 예제를 보자.

### c++

#include <stdio.h>

static void externalCall();

class A
{
public:
    A() {
        printf("In Constructor A\n");
        call();
    }
    virtual ~A() {
        printf("In Destructor A\n");
        call();
        externalCall();
    }
    virtual void call() {
        printf("    Call A is called\n");
    }
    void callFromA() {
        printf(" > In %s \n", __func__);
        call();
    }
};

class B : public A
{
public:
    B() {
        printf("In Constructor B\n");
        call();
    }
    ~B() {
        printf("In Destructor B\n");
        call();
        externalCall();
    }
    void call() {
        printf("    Call B is called\n");
    }
};

static A* global;

static void externalCall()
{
    printf(" > In %s \n", __func__);
    global->call();
}

int main()
{
    A* local = new B;
    printf("In %s \n", __func__);
    local->call();
    local->callFromA();

    global = local;
    externalCall();

    delete local;
}



위 예제를 실행해보면 재밌는 결과를 얻을 수 있다.

In Constructor A ---- 1
    Call A is called
In Constructor B
    Call B is called
In main  ---- 2
    Call B is called
 > In callFromA
    Call B is called
 > In externalCall
    Call B is called
In Destructor B ---- 3
    Call B is called
 > In externalCall
    Call B is called
In Destructor A
    Call A is called
 > In externalCall
    Call A is called


1. 실행 결과에서 먼저 생성자의 호출 순서를 알 수 있다.

생성자의 경우 부모클래스(A)의 생성자가 호출된 후, 자식 클래스(B)의 생성자가 호출된다.

이 때, 자식 클래스의 생성자가 호출되기 이전에 부른 call method는 부모 클래스의 method가 호출됨을 알 수 있다.


2. main에서는 예측한 것과 같이 자식 클래스(B)의 method만이 호출됨을 알 수 있다.

설령, 부모클래스의 메소드를 거쳐서 호출하더라도(callFromA) 자식 클래스의 인스턴스이기 때문에 자식 클래스의 메소드가 호출된다.


3. destructor의 호출 순서는 생성자의 경우와 반대로 자식 클래스의 소멸자 -> 부모클래스의 소멸자 순으로 불린다.

이 때, 자식 클래스의 소멸자 안에서 호출한 call method는 자식 클래스(B)의 메소드가 불린 반면,

부모 클래스(A)의 소멸자 안에서는 자식 클래스(B)의 call method가 아닌, 부모 클래스(A)의 call method가 불린 것을 알 수 있다.


externalCall의 결과만 보면 자식 클래스(B)의 소멸자에서 불릴때와 부모클래스(A)의 소멸자에서 불릴때 다른 메소드가 불리게 된다는 것을 알 수 있다.

소멸자가 부른 외부 함수(또는 다른 클래스의 함수)가 이를 모르고 호출할 경우, 의도하지 않은 동작을 할 수도 있기 때문에,

소멸자가 호출하는 함수가 다시 자신의 메소드를 호출하지 않도록 주의할 필요가 있을 것 같다.


PS>

쓰고 보니 이미 Effective C++에서 다루고 있는 내용이었다.

다시 읽어봐야겠다. ㅠ_ㅠ


http://www.artima.com/cppsource/nevercall.html

신고
Posted by 소혼
작년에 malloc vs calloc이라는 글을 적었습니다.
그때는 급작스런 궁금증때문에 인터넷 검색+발번역을 했는데,
이번에는 new에 대해 신경쓰지 못했던 부분을 알게되었습니다.

흔히, new 나 malloc으로 메모리를 할당받고 난 후, 제대로 메모리가 할당되었는지 체크를 하지않는 경우가 많습니다.
하지만, 메모리가 부족한 상황은 얼마든지 나올 수 있으므로 체크를 해야 맞겠죠.

그렇다면, 어떻게 체크해야 할까요?
임베디드의 세상에서 malloc으로 메모리를 할당받은 후에는 null인지 확인하는 것이 보편적이라는 이야기를 들었습니다.

그렇다면 new는?
C++을 배울때 제대로 공부하신 분들은 이 질문의 답을 정확히 알것입니다.
네, new 는 0을 반환하지 않습니다. 일부러 그렇게 만들지 않는한 말이죠.(오퍼레이터 오버로딩)
대신 bad_alloc exception을 발생시킵니다.
따라서, 메모리할당 여부를 굳이 확인하려면, 아래와 같이 처리해야 합니다. (예제소스)
// bad_alloc example
#include <iostream>
#include <new>
using namespace std;

int main () {
  try
  {
    int* myarray= new int[10000];
  }
  catch (bad_alloc& ba)
  {
    cerr << "bad_alloc caught: " << ba.what() << endl;
  }
  return 0;
}

신고
Posted by 소혼
TAG C++, new
C++에서 static을 다소 헷갈리게 만드는 요소가 있긴 하지만,

header에서 static 변수를 선언해서는 안된다.

static : 지역 변수임을 알려주는 키워드 (C++ 클래스 내부의 static과 다른 의미이다.)

헤더에서 static을 선언하면 어떤 일이 일어날까?
간단한 예제를 만들어보았다. (컴파일 없이 손코딩했음)


common.h
static int gvalue = 10;


main.c
#include <stdio.h>
#include "common.h"
#include "b.h"

int main () {
    printf("%d\n", gvalue);
    call_b();
    printf("%d\n", gvalue);
    return 0;
}

b.h
void call_b();

b.c
#include "common.h"

void call_b() {
    gvalue++;
}

간단한 예제인데, 이 때 출력되는 값은 얼마일까?
10과 11이 출력되길 기대하겠지만 안타깝게도 10과 10이 출력이 된다.
이유는 gvalue가 main.c와 b.c에 각각 따로 존재하기 때문이다.
nm으로 바이너리를 까보면 gvalue를 위해 두개의 심볼이 존재하는 것을 알 수 있다.
즉, b.c가 증가시킨 gvalue는 main.c에 보이지 않는 b.c를 위한 지역변수가 된다.

그럼 이런 실수를 왜 할까?
C++로 넘어오면서 일부 책에서 #define을 쓰지 말고 const를 쓸것을 권장하고 있다. 이러한 상수형(?) 변수를 쓰는 것이 debugging에 도움이 될 수 있고 그다지 성능을 많이 잡아먹지 않을 것이라는 것은 안다.(제대로 짰을때에 한해서)
이렇게 만들어진 const를 그냥 header에 넣으면 분명 에러가 발생한다. 이유는 헤더가 동일한 상수형 변수를 여기저기 만들기 때문이다.운
거기서 포기하지 않고 static을 붙이면 컴파일은 되겠지만, 나중에는 찾기 어려운 재앙이 될 수 있다.
신고

'프로그래밍 언어 > C/C++' 카테고리의 다른 글

__builtin_expect 키워드  (0) 2010.09.10
gcc 업그레이드 (4.5.0)  (0) 2010.07.21
Header안에 static 변수  (0) 2010.02.21
디버깅하는 법 - 툴 - ltrace  (0) 2010.02.18
unresolved symbol 에 관하여  (0) 2010.01.27
[Python]파일명 바꾸기  (0) 2009.09.02
Posted by 소혼
TAG C, C++
이전버튼 1 이전버튼

티스토리 툴바