C++ virtual 관련

asleea의 이미지

#include<iostream>
using namespace std;
 
class C
{
public:
	virtual string toString()
	{
		return "class C";
	}
};
 
class B : public C
{
public:
	string toString()
	{
		return "class B";
	}
};
 
 
void displayObject(C* c)
{
	cout << c->toString().data() << endl;
}
 
int main()
{
	B b;
	C c;
	displayObject(&b);
	displayObject(&c);
 
	return 0;
}

virtual 함수 관련 공부하다가 궁굼한 부분이 생겨서 글을 올리게 되었습니다.

virtual이라는 키워드가 있을때와 없을때의 내부적으로 동적이 어떻게 다르길래 결과가 다른건가요 .?

그냥 제 지식으로는 virtual이라는 키워드가 없어도 원하는 각각의 오브젝트에 맞는 string이 출력되야 될꺼 같은데

책에는 동적결합 정적결합이라는 용어가 나와있던데 설명좀 부탁드립니다.ㅜㅜ

asleea의 이미지

글을 쓰고 올렸는데 저장이 안되서 막 누르다가 나갔다 들어오니 .;;;;;글이 똑같은게 3개가 올라왔네요 .죄송합니다.

삭제하려고 찾아봤는데 .;;;삭제 방법이 없다는거 같은데

shint의 이미지

//
절대 용어나 내용에 대한 신뢰성은 없습니다.
틀린 부분은 지적해 주세요.

//
부모에 virtual 이 있을경우. 묵시적 형변환이 일어난것과 같은 현상을 발견했습니다.
게다가 virtual 이 존재할 경우. 형변환이 되지 않는 현상도 발견했습니다. (이것으로 virtual 자체가 묵시적 형변환 이라는 생각이 듭니다.)

virtual은 virtual function table에서 동적 바인딩을 통해 함수가 실행된다고 하는데요.
결과를 보면. 형변환(참조변환??)과 비슷한 기능으로 여겨집니다.

virtual이 붙으면 정적 바인딩(참조변환 불가능)
virtual이 안붙으면 동적 바인딩(참조변환 가능)' 이라는 생각도 듭니다.

여기서 알게 되는 가장 중요한 한가지.
virtual이 없으면. 내 맘대로 형변환이 가능한 함수 구현이 가능하다 입니다.

참고로. 이거 code 구분자가 [ % 군요... ㅡ_ㅡ;;;

[%

#include

//C vfn -> B fn
class C
{
public:
virtual void fn()
{
printf("class C: this[%x] this->fn[%x]\n", this, this->fn);
return ;
}
};

class B : public C
{
public:
void fn()
{
printf("class B: this[%x] this->fn[%x]\n", this, this->fn);
return ;
}
};

void displayObject(C* c)
{
printf("displayObject: C* c[%x], C* *c[%x], C* &c[%x], c->fn[%x]\n", c, *c, &c, c->fn);
c->fn();
((B*)c)->fn(); //virtual을 붙이면 형변환이 보여지지 않는다.
}

int main()
{
B b;
C c;

printf("B b[%x], &b[%x]\n", b, &b);
displayObject(&b);
printf("\n");

printf("C c[%x], &c[%x]\n", c, &c);
displayObject(&c);
printf("\n");

return 0;
}

//------------------------------
//virtual 이 없을 경우
//------------------------------
B b[371acc], &b[12ff7c] b.fn[401140] ((C*)&b)[12ff7c] ((C*)&b)->fn[40113b] //함수 주소가 다릅니다. (형변환이 적용되어 보입니다.)
displayObject: C* c[12ff7c], C* *c[426fcc], C* &c[12ff28], c->fn[40113b] //C* c로 b의 주소가 왔습니다. c->fn은 ((C*)&b)->fn의 형변환된 주소로 출력됩니다.
class C: this[12ff7c] this->fn[40113b] //각각의 클래스에서 b의 주소를 참조하며 각각의 함수를 호출합니다.
class B: this[12ff7c] this->fn[401140]

C c[cc], &c[12ff78] c.fn[40113b] ((B*)&c)[12ff78] ((B*)&c)->fn[401140] //함수 주소가 다릅니다. (형변환이 적용되어 보입니다.)
displayObject: C* c[12ff78], C* *c[426fcc], C* &c[12ff28], c->fn[40113b] //C* c로 c의 주소가 왔습니다. c->fn은 c.fn의 주소로 출력됩니다.
class C: this[12ff78] this->fn[40113b] //각각의 클래스에서 c의 주소를 참조하며 각각의 함수를 호출합니다.
class B: this[12ff78] this->fn[401140]

//------------------------------
//부모에 virtual 이 있을 경우
//------------------------------
B b[423088], &b[12ff7c] b.fn[401159] ((C*)&b)[12ff7c] ((C*)&b)->fn[401159] //함수 주소가 같습니다. (형변환이 적용되지 않아 보입니다.)
displayObject: C* c[12ff7c], C* *c[423088], C* &c[12ff28], c->fn[401159] //C* c로 b의 주소가 왔습니다. c->fn/ b.fn/ ((C*)&b)->fn은 모두 주소가 같습니다.
class B: this[12ff7c] this->fn[401159] //b의 주소를 참조하여 B클래스와 함수만 호출 됩니다.
class B: this[12ff7c] this->fn[401159]

C c[42201c], &c[12ff78] c.fn[401159] ((B*)&c)[12ff78] ((B*)&c)->fn[401159] //함수 주소가 같습니다. (형변환이 적용되지 않아 보입니다.)
displayObject: C* c[12ff78], C* *c[42201c], C* &c[12ff28], c->fn[401159] //C* c로 c의 주소가 왔습니다. c->fn/ c.fn/ ((B*)&c)->fn은 모두 주소가 같습니다.
class C: this[12ff78] this->fn[401159] //c의 주소를 참조하여 C클래스와 함수만 호출 됩니다.
class C: this[12ff78] this->fn[401159]

//------------------------------
//부모 자식 모두 virtual 일 경우
//------------------------------
B b[423088], &b[12ff7c] b.fn[401159] ((C*)&b)[12ff7c] ((C*)&b)->fn[401159] //함수 주소가 같습니다. (형변환이 적용되지 않아 보입니다.)
displayObject: C* c[12ff7c], C* *c[423088], C* &c[12ff28], c->fn[401159] //C* c로 b의 주소가 왔습니다. c->fn/ c.fn/ ((C*)&b)->fn은 모두 주소가 같습니다.
class B: this[12ff7c] this->fn[401159] //b의 주소를 참조하여 B클래스와 함수만 호출 됩니다.
class B: this[12ff7c] this->fn[401159]

C c[42201c], &c[12ff78] c.fn[401159] ((B*)&c)[12ff78] ((B*)&c)->fn[401159] //함수 주소가 같습니다. (형변환이 적용되지 않아 보입니다.)
displayObject: C* c[12ff78], C* *c[42201c], C* &c[12ff28], c->fn[401159] //C* c로 c의 주소가 왔습니다. c->fn/ c.fn/ ((B*)&c)->fn은 모두 주소가 같습니다.
class C: this[12ff78] this->fn[401159] //c의 주소를 참조하여 C클래스와 함수만 호출 됩니다.
class C: this[12ff78] this->fn[401159]

----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.

매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.

각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com

klyx의 이미지

보통 C++에서 정적이라 함은 컴파일타임에 결정되는 것이고 동적이라 함은 런타임에 결정되는 것을 가리킵니다.
호출되는 함수가 비가상함수인 경우에는 정적으로 호출되며, 이말은 실제 런타임에서 어떤 타입을 썼던,
호출되는건 컴파일타임에 결정된 타입의 멤버함수가 된다는 뜻입니다.

displayObject()함수는 C클래스의 포인터를 받기 때문에,
만약에 toString()함수가 비가상함수라면 넘겨진 포인터가 실제로는 C로부터 파생된 클래스(여기서는 B)의 객체라고 해도
무조건 C클래스의 함수(즉 C::toString())을 호출합니다.
컴파일 타임에는 명시적으로 주어진 인자의 타입(C*)만 알 수 있기 때문입니다.

반대로 toString()함수가 가상함수인 경우에는 동적 바인딩이 이루어지고, 따라서 런타임에 결정되는 타입에 따라서 결과가 달라질 수 있습니다.
비록 displayObject()는 C*로 인자를 받지만, 여기에는 C로부터 파생된 클래스들도 넘겨질수 있고, 실제로 호출되는 것은 실제 객체의 함수가 됩니다.
따라서 첫번째 displayObject()에서는 B::toString()이 호출되고 두번째 displayObject()에서는 C::toString()이 호출됩니다.

익명 사용자의 이미지

virtual을 쓰면 vtable이라는 함수포인터 집합을 통해서 접근하기 때문이죠.
virtual을 쓰지 않는다면 그냥 일반 함수 호출하듯이 하는거구요.

class bar : public foo
와 같이 상속될때

foo *a = new bar;
라고 하면 일단 a에 할당된 실제정보는 bar인지 몰라도 자료형은 foo *입니다.

foo에도 func()가 정의되어 있고 bar에도 func()가 정의되어 있겠지만
a는 자료형이 foo *이니까 자료형에 맞게 그냥 foo의 func()를 호출하는 것이 당연하죠.

그런데 virtual이라는 키워드를 사용해서 vtable이라는게 존재한다면
객체가 생성될때 vtable이 세팅됩니다.

그러니까 virtual이 선언되어 있을
foo라는 객체에 (*virtual_func)()같은 함수포인터가 있는 것이라 상상할 수 있는데
foo 생성자는 virtual_func의 값을 foo의 func로 해놓을테고
bar 생성자는 virtual_func의 값을 bar의 func로 해놓겠죠.

그럼 foo *a = new bar 라고 하면
a는 foo *라는 자료형이지만
그 foo가 가지는 virtual_func의 값은 bar의 func입니다.
new bar라고 bar의 생성자로 만들었으니까요.

이렇게 되었을때 a의 virtual_func를 호출한다면
bar의 func이 호출되는 것이죠.

댓글 달기

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