Here is how I understood not just what virtual functions are, but why they're required:

Let's say you have these two classes:

class Animal { public: void eat() { std::cout << "I'm eating generic food."; } }; class Cat : public Animal { public: void eat() { std::cout << "I'm eating a rat."; } };

In your main function:

Animal *animal = new Animal; Cat *cat = new Cat; animal->eat(); // Outputs: "I'm eating generic food." cat->eat(); // Outputs: "I'm eating a rat."

So far so good, right? Animals eat generic food, cats eat rats, all without virtual.

Let's change it a little now so that eat() is called via an intermediate function (a trivial function just for this example):

// This can go at the top of the main.cpp file void func(Animal *xyz) { xyz->eat(); }

Now our main function is:

Animal *animal = new Animal; Cat *cat = new Cat; func(animal); // Outputs: "I'm eating generic food." func(cat); // Outputs: "I'm eating generic food."

Uh oh... we passed a Cat into func(), but it won't eat rats. Should you overload func() so it takes a Cat*? If you have to derive more animals from Animal they would all need their own func().

The solution is to make eat() from the Animal class a virtual function:

class Animal { public: virtual void eat() { std::cout << "I'm eating generic food."; } }; class Cat : public Animal { public: void eat() { std::cout << "I'm eating a rat."; } };

Main:

func(animal); // Outputs: "I'm eating generic food." func(cat); // Outputs: "I'm eating a rat."

Done.

https://stackoverflow.com/questions/2391679/why-do-we-need-virtual-functions-in-c

 

 

 

 

Why do we need virtual functions in C++?

I'm learning C++ and I'm just getting into virtual functions. From what I've read (in the book and online), virtual functions are functions in the base class that you can override in derived class...

stackoverflow.com

binding 과 관련된 일이다..필요하면 early binding/late binding 찾아봐

https://www.geeksforgeeks.org/virtual-function-cpp/

이런 코드를 봤는데

소스 코드

#include <iostream>

class Foo {
public:
  int bar;
  Foo(int num): bar(num) {};
};

int main(void) {
  std::cout << Foo(42).bar << std::endl;
  return 0;
}

여기서 : bar(num)처럼 쓰는건 뭔가요?

저렇게 쓰는 걸 처음 봤는데 다른 함수를 호출하는 건가요? 아니면 생성자에서 쓴걸 보면 생성자랑 관련 있는 건가요?


Foo(int num): bar(num) 은 초기화 리스트라고 하고, 멤버 변수 bar를 num으로 초기화하는 역할을 합니다.

//초기화 리스트
Foo(int num): bar(num) {};

//함수에서 초기화
Foo(int num)
{
   bar = num;
}

그냥 생성자 함수{} 내에서 초기화하는 것과, 이렇게 초기화 리스트를 쓰 는것의 차이는

초기화 리스트에서 초기화를 하는 경우, 생성자가 호출될 때 객체의 생성과 초기화가 한 번에 이루어집니다.

생성자 함수 내{}에서 초기화를 하는 경우, 객체가 생성되어, default생성자로 초기화된 상태에서 다시 한 번 할당받게 하게 됩니다. 이 경우엔 default할당-유저할당의 2단계를 거치게 돼서 오버헤드가 생깁니다.

초기화 리스트를 써야만 하는 상황은 크게 다음과 같습니다

  • 클래스가 레퍼런스를 멤버로 가질 때
  • non static const멤버가 있을 때
  • default 생성자가 없을 때
  • base class를 초기화할 때
  • 생성자 파라미터의 이름이 데이터 멤버랑 같을 때(이 경우는 this를 써서 해결할 수도 있습니다)

예를 들면

class MyClass
{
    public:
        int &i; //레퍼런스 멤버. 초기화 리스트를 써야 함
        int b;
        //Non static const 멤버. 초기화 리스트를 써야 함
        const int k;  

    //생성자 파라미터의 이름이 데이터 멤버랑 같음. 초기화 리스트를 쓸수 있음(선택 가능)
    MyClass(int a, int b, int c):i(a),b(b),k(c)
    {
        /*
        초기화 리스트를 쓰고 싶지 않은 경우
        this->a = a
        같이 써야 함
        */
    }
};

class MyClass2:public MyClass
{
    public:
        int p;
        int q;
        //base class인 MyClass가 default생성자가 없기 때문에 무조건 초기화 리스트에서 초기화해줘야 함
        MyClass2(int x,int y,int z,int l,int m):MyClass(x,y,z),p(l),q(m)
        {
        }

};

int main()
{
    int x = 10;
    int y = 20;
    int z = 30;
    MyClass obj(x,y,z);

    int l = 40;
    int m = 50;
    MyClass2 obj2(x,y,z,l,m);

    return 0;
}


서버


1. 서버

-Serv.h


-Serv.cpp

 

-main.cpp(Server)


2. 클라이언트


-main.cpp


mutex를 걸어주는 방법에는 critical section (함수?) 도 있다고 한다.....

EVENT 생성시 마지막 인자로 들어가는 TEXT는 안주는 것이 좋다.

EVENT TEXT가 같으면 다른 프로그램에서도 같은 EVENT로 인식을 하기 때문에


-thread 종료를 시키는 방법


thread들은 while문을 돌때 조건으로 flag check를 한다.

delete(소멸자에서) 할때 flag set을 한뒤 waitEvent

thread에서 set된 flag를 확인하고 while문을 빠져나온다.

while문을 빠져나와 thread를 빠져 나오기전 setEvent를 해주면 이벤트를 기다리던 delete함수가 메모리 해제를 시작한다.



'Programming Language > C++' 카테고리의 다른 글

Why do we need virtual functions in c++?  (0) 2019.04.05
생성자 뒤에 콜론(:)  (0) 2017.03.08
MultiThread Programming  (0) 2016.08.17

소켓통신: http://www.codeproject.com/Articles/11740/A-simple-UDP-time-server-and-client-for-beginners

mutex: https://msdn.microsoft.com/ko-kr/library/windows/desktop/ms686927(v=vs.85).aspx

callback함수: http://stackoverflow.com/questions/10555566/difference-between-c11-stdbind-and-boostbind

"What is A?"
라는 문장을 변수 a에 넣고 싶다.

그럼 
char* s=malloc(sizeof(char)*10);
scanf("%s",s);
printf("s:%s\n",s);

이런식으로 하면 될까?
위의 결과는
s: What
이다.



"what is A"라는 문장을 사용자가 입력하면 버퍼에 그대로 저장이된다.
그런 뒤 scanf를 사용하면 버퍼에서 공백이 있는 곳까지 잘라 변수에 저장하게 된다. 

프로그램 코드와 실행 결과











이런식으로 코딩하게 되면 "What(space)is(space)A(Enter)"를 입력했을 때
s=What
s1=is
s2=A
가 된다. 

What is A 를 입력한 뒤 다른 입력 없이도 scanf함수를 사용할 때마다 buffer에서 읽어와서 변수에 저장하는 것이당


실행결과




그럼 공백도 포함해서 s에 넣고 싶으면 어쩌나..

fgets를 사용하면 된다. 
fgets(char* s, int size, File* stream);
이런 형태인데 stream으로부터 한줄씩(개행문자가 오기 전까지) 읽어서 s에 넣는 함수이다.
fgets함수 정리는 다음에... 





http://kaspyx.kr/2

int index=0;

printf("%02x",index);


//%(02)x=%(출력하고자하는 자리수)x


printf('%02x",index)의 결과=00

printf('%05x",index)의 결과=00000


Slider가 움직이는대로 ProgressBar움직이게 하기


char buff[100]="note";
char* pstr="book";
char* p;
strcat(buff, pstr);
puts(buff);
strncat(buff, "12345",3);
puts(buff);
strcat(p,buff);        //오류: 쓰레기 주소에 값을 넣으려고 시도

strcat(pstr, buff);    //오류: 상수값에 값을 집어넣으려고 시도


alloca => stack 세그먼트 영역에 동적메모리 할당

장점: 함수 호출, 반환시 스택 영역을 사용하는데 스택영역에 동적메모리를 할당하면 함수 반환시 메모리가 자동 해제되기때문에 

free할 필요없음

2차원 배열을 선언 후 함 수 이자로 넘겨줄 필요가 있는 경우




이케 받으면 오류가 난다.

그 이유는?
이중배열은 실제로 메모리에 저장될 때는 일차원으로 저장된다. 

int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};


이렇게 선언한 배열은 


int a[12]={1,2,3,4,5,6,7,8,9,10,11,12};


라기 보다는 


int a[3]={int[4], int[4], int[4]}


이것과 같다고 볼 수 있다.


무슨말이냐






이중 배열의 주소체계는 이러하다. 

a자체는 총 3개의 int[4]원소를 가진 배열의 시작주소를 나타내고

a의 첫번째 원소 a[0]( *(a+0)과 같음 )=int[4]={1,2,3,4}

      두번째 원소 a[1]=int[4]={5,6,7,8}

      세번째 원소 a[2]=int[4]={9,10,11,12}가 된다. 

따라서 a[0]의 주소와 a[1]의 주소는( 4byte(int 크기)*4=)16byte 차이가 난다. 


a[0]은 총 4개의 int원소를 가진 배열의 시작주소를 나타내므로 

a[0]의 첫번째 원소 a[0][0]( *(*(a+0)+0)과 같음 )=int={0}

         두번째 원소 a[0][1]=int={1}

                        ...

과 같다. 

따라서 a[0][0]의 주소와 a[0][1]의 주소는 4byte차이가 난다. 


이를 코드로 확인해보면 



>>결과


이케 된다. 

a의 주소와 그 다음 주소 값을 확인해보면 16진수로 48byte(3*(int(4byte)*4))=(0x30) 차이가 나는 것을 알 수 있다

a의 첫번째 원소(*a=a[0])의 주소값과 두번째 원소(*(a+1)=a[1])의 주소값은 16byte(int*4)=(0x10) 차이가 나고

a[0]의 첫번째 원소(*(*(a+0)+0)=a[0][0])의 주소값과 두번째 원소(*(*(a+0)+1)=a[0][1])의 주소값은 4byte(int)=(0x04) 차이가 난당


여기서 나는 헷갈렸던게 a는 int 포인터 타입의 변수인줄 알았다.

즉 a는 배열을 메모리에 할당하고 그 주소값만 가지고 있는 변수라고 생각했는데

그게 아니라 그냥 그 배열 자체를 나타내는 변수였던 것이다

이게 무슨 말이냐면 변수를 사용할 때 

int a=4;

이케 선언하면 4라는 값을 메모리에 할당한 후 그 메모리의 주소값을 나타내는 애가 a이다. 

a라는 변수를 사용해서 4를 할당해준 메모리에 접근할 수 있는 것이다. 

(이게 변수를 만들어 주는 이유. 우리가 항상 메모리 주소를 기억하고 사용하기 불편하므로 변수를 사용해서 쉽게 사용하는 것)

a는 4를 갖고 있는 메모리자체라고 볼 수 있음

나는 이걸 a는 4라는 값을 갖는 int 자체를 나타내는 변수 라고 표현했다.

근데 포인터형 변수는 그 메모리 자체를 나타내는 변수가 아니라 메모리 주소값만을 갖는 변수이다

int* b=&a

라고 선언하면 a는 메모리의 주소값을 포인터형 변수인 b에게 알려주는 것이다.



그림을 보면 a는 4값을 담고 있는 메모리이고 b는 4를 담고 있는 메모리 주소를 담는 메모리이다.(아...쓰면서도 헷갈려)

b는 0xb4라는 주소를 따로 갖는 4를 담는 메모리와는 별개의 변수이지만 a는 4를 담는 메모리 자체인것이다.

부디 나중에 보면서도 이해되길....


다시 처음으로 돌아가서 왜

함수에서 

int a[3][4]로 선언한 배열을 

int** arr 로 받으면 에러가 나는가?


a는 배열을 가지고 있는 메모리 자체를 나타내는 변수고

arr은 정수값을 갖는 메모리를 찾아가기 위한 주소 값을 갖는 포인터형 변수이기 때문이다.





arr로 넘어오는 것은 a의 주소값인 0x00 이다. 

arr은 0x64번지에 메모리를 할당해주고 0x00이라는 주소값을 담고 있는 변수!

또 arr은 이중 포인터이기 때문에 처음에 0x00을 따라간 후에 0x00번지에 담겨 있는 값도 

8byte의 주소값(64bit에선 8byte, 32bit에선 4byte)으로 인식을 한다. 

0x00번지부터 0x08번지까지에 있는 binary값을 읽은 다음 그값을 16진수의 주소(0x21)로 인식을 하는 것이다. 

그래서 이를 따라간 다음 int 값으로 읽기 위해 4byte의 binary 값을 읽으면 값이 이상해진다.

(간단하게 그림을 나타내기 위해 정수값을 주소값으로 변환하는걸 그림에서는 야매로 0x21로 나타냈는데 

실제 컴퓨터에서는 정수값 1과 2를 연달아 binary값으로 읽은 다음 16진수 주소값으로 바꾸면

0x0000000200000001이 된다. 

정수값 1은 0x00000001로 저장 되고 정수값 2는 64bit에서는 0x00000002로 저장되는데 

little endian으로 8byte를 읽으면서 0x0000000200000001 이 되는 것이다)

그래서 제대로 넘겨주기 위해선



이케 써야된다 



뭐가 다르냐면....
int(*arr)[4]로 넘겨주는 것은 
arr이 가리키고 있는 건(*arr)
"int형 데이터 4개를 갖고 있는 배열(int[4]"을 담고 있는 메모리 자체라는 것을 알려주는 것이다. 
(*arr)이 int형 데이터 4개를 갖는 배열이라는 것을 알기 때문에 (*arr)은 16byte의 크기라는 것도 알고 있고
(*arr)에서 1을 더하면 arr주소에서 16byte를 더한 메모리로 이동할 수 있는 것이다. 
또 (*arr)이 int형 데이터를 4개 가지고 있는 배열이라는 것도 알기 때문에 (*arr)의 첫번째 원소를 읽기 위해서 
(*arr)[0]을 하면 시작 주소에서부터 int의 크기인 4byte를 읽을 수도 있다.
메모리 자체에 접근 가능
여기서 또 헷갈렸던게....@.@

내가 디버깅하며 테스트한 코드




디버깅하며 확인한 변수의 주소와 그 안에 들어있는 값


>>결과 




int(*arr2)[4]로 넘겨준 arr2를 보면 0x000000020bff8fbd0번지에 할당되어 

a의 주소값 0x000000020bff8fbf8을 갖고 있는 포인터형 변수라는 것을 알 수 있다. 

따라서 arr2의 사이즈는 8byte (64bit운영체제에서의 주소크기)이고 arr2가 가리키는 것은(0x000000020bff8fbf8번지에 있는 데이터=(*arr))

int[4]라는 것을 알려줬기 때문에 *(arr2)의 사이즈는 16byte라는 것을 알고 있다.

그리고 *(arr)이 가리키는 데이터(0x000000020bff8fbf8번지에 있는 데이터=*(*arr))는 int형이니까 4byte로 출력된다. 

(arr2=*arr2=*(*arr2)인 이유는 arr2가 이중 배열을 가지고 있는 메모리 자체를 나타내는 변수 이기 때문. 위에 설명해놨다)

이렇게 잘 넘어와서 넘겨받은 배열의 내용이 잘 출력된다.

반면 int** arr로 받은 arr을 보면 0x000000020bff8fbd0번지에 할당되어 
a의 주소값 0x000000020bff8fbf8을 갖고 있는 포인터형 변수라는 것을 알 수 있다. 
arr의 사이즈는 8byte이고 arr이 가리키는 것은 다시 포인터형이기 때문에 *arr의 사이즈는 주소크기인 8byte가 되고 
*arr을 int형 데이터가 4개 담겨있는 배열로 읽는 것이아니라 
*arr에 있는 값을 주소 형태인 8byte단위로 읽는다. 
그래서 *arr에 있는 데이터  (4byte 숫자 1(0x00000001)과 arr[0][1]에 있던 4byte 숫자 2(0x00000002))를 읽어
0x0000000200000001 형태의 주소로 읽는다. (*arr=0x0000000200000001)
(*arr+1(arr[1])은 그 다음 8byte 데이터인 0x0000000400000003(정수 3과 정수 4를 8byte 주소로 읽은 것)으로 읽는 것 봐라...절레절레...)
*(*arr)(=0x0000000200000001이 가리키는 데이터)는 당연히 없다. 



이중배열을 
int** b;
이렇게 선언해주고 동적할당 해주면 
함수인자로 
int printArr(int** arr)
이렇게 넘겨줘도 에러가 안난다.
무슨 차이인지는 2탄에 정리....



+ Recent posts