[완료] C++에 대해, 부모클래스와 멤버함수만 추가한 자식클래스간의 변환에 대해서

klara의 이미지

class A {
public:
	A(int value):v(value) {}
	int value() const {return v;}
private:
	int v;
};
 
class B : public A {
public:
	B():A(0) {}
	bool isZero() const {return value() == 0;}
};

위와 같은 코드가 있습니다. B는 A를 상속받되, 새로운 멤버변수는 추가하지 않고 멤버함수만을 추가하였습니다.
이때,
A a(1);
B &b = static_cast<B&>(a);
if (b.isZero()) {
...
}

위와 같은 코드는 안전한 코드일까요?

특히 클래스 A의 경우 소멸자가 비가상함수인데요, B에서는 멤버변수 추가가 없으므로,

A *a = new B;
delete a;

와 같이 하여도 메모리누수의 걱정도 없을 듯한데, 무언가 문제되는 것이 있을까요?
winner의 이미지

딱히 흥미가 아닌 이상 이렇게 하는 이유는 없을 거 같고요.
A, B 모두 가상 멤버 함수가 없느니만큼 실제적으로는 문제가 발생하지는 않겠지만 그렇더라도 표준에서는 이런 부분에 대해서 언급을 하지 않거나 언급한다면 비정의 동작이라고 할 것 같아요.

klara의 이미지

제 실력이 부족한 탓인지 모르겠지만, '흥미'외에 실제로도 사용하게 되는 경우가 있었습니다.
STL의 클래스들 처럼 부모 클래스가 제가 만든 클래스가 아니라 사용하는 라이브러리에서 제공하는 클래스이고, 상속을 염두에 두지 않았을 경우입니다.
좀더 구체적인 예를 들어 보면...

namespace Lib {
 
class Data {
	...
};
 
class Klass {
	...
public:
	void setData(const Data &data) {d = data;}
	const Data &data() const {return d;}
private:
	Data d;
};
 
}

여기서 namespace Lib 는 외부 라이브러리이기 때문에 제가 수정할수가 없을때, Data 클래스를 확장하고 싶은 경우,
class MyData : public Lib::Data {
public:
	void isValid() const;
};

와 같이 선언하여
Lib::Klass klass;
...
if (static_cast&lt;const MyData&amp;&gt;(klass.data()).isValid()) {
	...
}

와 같이 이용하고 싶은 경우입니다.
물론 이를 해결하기 위해서 상속하지 않고 다른 방법이 있다는 것을 알고 있습니다만, 실제로 MyData는 Lib::Data 그자체이므로, 예를 들어 MyData에 멤버변수로 Data를 선언하여 Data MyData::data() const 와 같은 멤버함수로 접근하게 하는 것은 번거롭기도하고 실제로 의미상으로도 적합하지 않아 보입니다.
winner의 이미지

우선 제가 말씀드릴 수 있는 것은 C++ 언어는 이런 방식을 받아들이지 않는다는 것입니다.
비슷한 예로 STL의 string의 경우 상속될 여지가 많음에도 표준화 위원회가 가상소멸자를 넣지 않은 것은 고심 끝에 이루어진 것으로 알고 있습니다.
원래 Data를 만든 분이 어디까지 고민을 하셨을런지 모르지만 만일 C++ 언어의 방식을 받아들이고, Data를 만드신 분을 존중한다면 다른 방식이 적합하다고 봅니다. 만일 자신이 표현하고자 하는 방식이 그 이상의 가치를 지닌다면 적절히 실험 후에 임의로 변경할 수 있겠지요. 언제나 표준이나 기본 library 설계자의 의견이 옳은 것도 아니고, 그들이 생각하지 못하는 바도 생길 수 있으니까요. 하지만 이것이 단지 여기서 통용되는 기술이고, 다양하게 사용되는 형태의 기술이 아니라면 C++ 구현과 Data의 변화에 따라 같이 맞춰주는 것을 감내하셔야 합니다. 만일 다양하게 통용될 수 있는 기술이라면 새롭게 C++ 표준에 넣고자 제안을 할 수도 있고, Data 설계자와 협의를 할 수도 있겠지만 말이죠. 제가 보기에 그정도 의미를 담고 있지는 않아 보입니다.

제가 추천하는 바는 멤버변수의 추가가 없으므로 파생클래스를 만들지 말고 적절한 전역함수를 만들어서 쓰는 것이 어떨까 싶습니다. 파생클래스를 만들어 놓고, 기반클래스를 파생클래스로, downcasting을 그것도 적합하지 않은 방식으로 해야 한다는 것 자체가 객체지향에 어긋나는 부자연스러운 구조라고 봅니다.

philnet의 이미지

해당 외부 라이브러리의 클래스의 소멸자가 비가상함수인 것은 기본적으로 상속해서 쓰지 말라는 선언이라고 생각합니다.

말씀하신 바와 같이, 다른 가상 함수도 없고 상속 받은 클래스에 멤버도 없기 때문에, 현재 본인이 쓰시는 경우에는 문제가 없을 수 있겠습니다만, 혹시 이 코드를 사용하게 되는 다른 개발자의 경우는 별 생각 없이 멤버 변수를 추가하거나, 혹은 이를 다시 상속 받아서 쓰거나 하는 과정에서 여러 문제를 발생시킬 여지가 아주 높다고 생각합니다. (다른 개발자의 범주에는 어느 정도 시간이 지난 후에, 이런 내용을 잊어 버린 미래의 본인도 포함될 수 있을 겁니다.)

아주 임시로 테스트하는 용도 이외에 정식으로 사용하는 코드에서는 이와 같은 방법을 사용하지 않는 것이 여러 모로 좋을 것이라고 생각합니다. (만약 제가 같이 일하는 동료가 이런 방법을 쓴다면 한대 쥐어 박아서라도 못 쓰게 할 것 같습니다. ^^)

Scarecrow의 이미지

class foo {
public:
        void do_it() { cout << "I can do it." << endl; }
};
 
int main() {
        foo *temp = NULL;
        temp->do_it();
}
라고 해도 될까요?

라는 질문과 똑같은 느낌을 줍니다.

winner의 이미지

아마 이건 C++ 표준에 어긋나지는 않을 것 같기는 한데... 역시 쓸데없는 고민이었죠.

netionics의 이미지

일단 표준에서는 결과를 정의하지 않습니다.
멤버 변수는 추가가 안된 상황이지만 가상함수테이블, RTTI 혹은 컴파일러 구현자가 몰래 넣은 무언가가 메모리 누수보다 더 무서운 폭탄이 될 가능성이 있습니다.
이런 것 들을 처리하기 위해 소멸자에 보이지 않는 코드가 삽입된다는 것은 이미 아시리라 생각합니다.

:)

Fe.head의 이미지

STL을 상속받아 쓰지 말아라 라고 누군가 말한것 같은데...
누군지 모르겠군요.^^
-----------------------
과거를 알고 싶거든 오늘의 네 모습을 보아라. 그것이 과거의 너니라.
그리고 내일을 알고 싶으냐?
그러면 오늘의 너를 보아라. 그것이 바로 미래의 너니라.

고작 블로킹 하나, 고작 25점 중에 1점, 고작 부활동
"만약 그 순간이 온다면 그때가 네가 배구에 빠지는 순간이야"

klara의 이미지

답변주신분들 모두 감사드립니다.
결국 다른 이유때문에라도 저런 변태적인 형변환은 하지 않고 다른 방법을 이용하게 되었습니다.
전역함수에 대해서도 생각해보았지만, 단순히 '전역함수가 싫다'는 이유때문에 안쓰고 있었는데, 다음에 또 비슷한 경우가 생기면 전역함수나 래핑 클래스를 이용하도록 해야겠습니다.

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.