반응형

c++에서는 new 키워드를 사용해서 동적으로 메모리를 할당합니다.

그리고 이러한 메모리는 반드시 delete 키워드를 사용하여 메모리 할당을 해제합니다.

 

스마트 포인터(smart pointer)란 포인터처럼 동작하는 클래스 템플릿으로, 사용이 끝난 메모리를 자동으로 해제해 줍니다.

 

java에서의 가비지 컬렉션과 같은 역할을 해주는 것입니다.

 

C++11 이전에는 auto_ptr이라는 스마트 포인터를 사용하여 이 작업을 수행하였습니다.

하지만 C++11부터는 다음과 같은 새로운 스마트 포인터를 제공하고 있습니다.

 

  •  unique_ptr
  •  shared_ptr
  •  weak_ptr

 

1. unique_ptr 

 

하나의 스마트 포인터만 특정 객체를 소유하는 것입니다. 해당 스마트 포인터는 move 함수를 통해서 소유권을 이전할  수는 있지만 복사는 불가능합니다.

 

unique_ptr<int> ptr01(new int(5)); // int형 unique_ptr인 ptr01을 선언하고 초기화함.

auto ptr02 = move(ptr01);          // ptr01에서 ptr02로 소유권을 이전함.

// unique_ptr<int> ptr03 = ptr01;  // 대입 연산자를 이용한 복사는 오류를 발생시킴. 

ptr02.reset();                     // ptr02가 가리키고 있는 메모리 영역을 삭제함.

ptr01.reset();                     // ptr01가 가리키고 있는 메모리 영역을 삭제함.

 

 

예시코드입니다.

#include <iostream>

#include <memory>

using namespace std;

 

class Person

{

private:

    string name_;

    int age_;

public:

    Person(const string& name, int age); // 기초 클래스 생성자의 선언

    ~Person() { cout << "소멸자가 호출되었습니다." << endl; }

    void ShowPersonInfo();

};

 

int main(void)

{

    unique_ptr<Person> hong = make_unique<Person>("길동", 29);

    hong->ShowPersonInfo();

    return 0;

}

 

Person::Person(const string& name, int age) // 기초 클래스 생성자의 정의

{

    name_ = name;

    age_ = age;

    cout << "생성자가 호출되었습니다." << endl;

}

 

void Person::ShowPersonInfo() { cout << name_ << "의 나이는 " << age_ << "살입니다." << endl; }

 

실행 결과입니다.

 

생성자가 호출되었습니다.

길동의 나이는 29살입니다.

소멸자가 호출되었습니다.

 

 

자동으로 메모리를 해제해주기 때문에 개발자가 메모리에 대한 신경을 덜 써줘도 되는 장점이 있습니다.

 

2. shared_ptr

 

하나의 특정 객체를 참조하는 스마트 포인터가 총 몇 개인지 참조하는 스마트 포인터입니다.

 

아래는 예시 코드입니다.

shared_ptr<int> ptr01(new int(5)); // int형 shared_ptr인 ptr01을 선언하고 초기화함.

cout << ptr01.use_count() << endl; // 1

auto ptr02(ptr01);                 // 복사 생성자를 이용한 초기화

cout << ptr01.use_count() << endl; // 2

auto ptr03 = ptr01;                // 대입을 통한 초기화

cout << ptr01.use_count() << endl; // 3  

make_shared() 함수를 사용하면 shared_ptr 인스턴스를 안전하게 생성할 수 있습니다.

 

예시 코드입니다.

shared_ptr<Person> hong = make_shared<Person>("길동", 29);

cout << "현재 소유자 수 : " << hong.use_count() << endl; // 1

auto han = hong;

cout << "현재 소유자 수 : " << hong.use_count() << endl; // 2

han.reset(); // shared_ptr인 han을 해제함.

cout << "현재 소유자 수 : " << hong.use_count() << endl; // 1

 

3. weak_ptr 

 

하나 이상의 shared_ptr 인스턴스가 소유하는 개체에 대한 접근을 제공하지만 소유자의 수에는 포함되는 않는 스마트 포인터입니다.

 

 

4. enable_shared_from_this

 

 이 친구는 아래와 같은 형태로 사용된다.

class type : public std::enable_shared_from_this<type>
{
public:
    type(){ std::cout << __FUNCTION__ << std::endl;}
    ~type(){ std::cout << __FUNCTION__ << std::endl;}
     
    std::shared_ptr<type> getPtr()
    {
        //return std::shared_ptr<type>(this);
        return shared_from_this();
    }   
};


 

정상적인 동작을 위해서 사용한다.

 

아래코드를 보자

#include <iostream>
#include <memory>
 
class type
{
public:
    type(){ std::cout << __FUNCTION__ << std::endl;}
    ~type(){ std::cout << __FUNCTION__ << std::endl;}
     
    std::shared_ptr<type> getPtr()
    {
        return std::shared_ptr<type>(this);
    }   
};
 
int main()
{
    type *p = new type;
     
    std::shared_ptr<type> ptr1(p); //p object를 가르키는 shared_ptr 생성자 호출
    std::cout <<"ptr1.use_count(): " << ptr1.use_count() << std::endl;
 
    // ptr2에 ptr1에서 객체의 주소를 return 받아 할당한다.
    std::shared_ptr<type> ptr2 = ptr1->getPtr();
    std::cout <<"ptr2.use_count(): " << ptr2.use_count() << std::endl;
     
    return 0;
}


 

type이라는 클래스 하나를 동적선언하고 ptr1에 할당한다. 그리고 ptr1이 사용되지 않기에 메모리를 소멸시키게 되는데 그렇기 때문에 ptr2에서는 오류가 발생한다. (Segmentation fault) 

 

이를 방지하고자 사용된다.

 

#include <iostream>
#include <memory>
 
class type : public std::enable_shared_from_this<type>
{
public:
    type(){ std::cout << __FUNCTION__ << std::endl;}
    ~type(){ std::cout << __FUNCTION__ << std::endl;}
     
    std::shared_ptr<type> getPtr()
    {
        //return std::shared_ptr<type>(this);
        return shared_from_this();
    }   
};
 
int main()
{
    type *p = new type;
     
    std::shared_ptr<type> ptr1(p); //p object를 가르키는 shared_ptr 생성자 호출
    std::cout <<"ptr1.use_count(): " << ptr1.use_count() << std::endl;
 
    // ptr2에 ptr1에서 객체의 주소를 return 받아 할당한다.
    std::shared_ptr<type> ptr2 = ptr1->getPtr();
    std::cout <<"ptr2.use_count(): " << ptr2.use_count() << std::endl;
     
    return 0;
}

 

shared_ptr을 추가할 경우에는 std::enable_shared_from_this 클래스로부터 상속을 받게되는 구조이다.

 

출처: www.tcpschool.com/cpp/cpp_template_smartPointer
출처: https://chipmaker.tistory.com/entry/enablesharedfromthis-정리 [CHIPMAKER]

반응형
반응형

TCP 패킷의 특징은 데이터에 대해서 1. 연결지향성 2. 신뢰성 보장이 있습니다. 

무조건 데이터를 상대방에게 전송합니다. 하지만 받는 쪽에서 전체 데이터를 못받을 수 있다는 문제점이 있습니다.

 

아래는 대표적인 client.cpp 코드입니다.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <time.h>

#define MAXLINE 1024 //buf 크기
#define TOTALFORK 5 //클라이언트 수

void createClient(char *port, char *serverIP);
int main(int argc, char *argv[]) {
	if(argc != 3) {
		printf("Use %s ip_addr port\n", argv[0]);
		exit(0);
	}

	pid_t pids[TOTALFORK]; //client fork
	int runProcess = 0;
	
	while(runProcess < TOTALFORK) {
		sleep(1);
		pids[runProcess] = fork();

		if(pids[runProcess] < 0) {
			return -1;
		}
		
		if(pids[runProcess] == 0) {
			createClient(argv[2], argv[1]);
			exit(0);
		} else { //부모 프로세스
			printf("parent %ld, child %ld\n", (long)getpid(), (long)pids[runProcess]);
		}
		runProcess++;
	}
	return 0;
}

void createClient(char *port, char *serverIP) {
	struct sockaddr_in servaddr;
	int strlen = sizeof(servaddr);
	int sockfd, buf, cNum;//cNum 연결 번호

	if((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
		perror("socket fail");
		exit(0);
	}

	memset(&servaddr, 0, strlen);
	servaddr.sin_family = AF_INET;
	inet_pton(AF_INET, serverIP, &servaddr.sin_addr);
	servaddr.sin_port = htons(atoi(port));

	if(connect(sockfd, (struct sockaddr *)&servaddr, strlen) < 0) {
		perror("connect fail");
		exit(0);
	}
	
	srand((unsigned)time(NULL));
	buf = rand() % 100 + 1; //rand 값 생성
	write(sockfd, &buf, 4); //server로 전송
	printf("cleint value : %d\n", buf);
	read(sockfd, &buf, 4); //server에서 받아 옴
	printf("Server sum result : %d\n", buf);
	close(sockfd);
}

 

네트워크 상태에 따라서 패킷이 찢어져서 들어오게 된다면 어떻게 될까요

정답은 짤린 패킷이 여러번에 걸쳐서 입력되는 형태 입니다. 

 

이러한 문제를 해결하기 위해서 while루프, length, offset을 사용해야합니다.

아래 처럼 offset과 length를 통해서 받은 만큼 표시를 해주며, 원하는 크기까지 받도록 무한 루프를 구성해야 합니다.

 int length = sizeof(Data) + strlen(name_msg);  //
 int offset = 0;
        while(1){
            int n = send(sock, data + offset, length, 0);

            if(n == -1){
                if(errno == EINTR) // signal interrupted
                    continue;
                else if(errno == EBADF)
                    cout<< "reject in network"<<endl;
                else if(errno == ENOTSOCK)
                    cout<< "sock err"<<endl;

                return (void*) - 1;
             }
            offset += n;
            length -= n;
            if(length == 0)
                break;
            
        }

 

그렇기 때문에 만약 서버라면 클라이언트별로 버퍼를 구성해주어야 합니다. 

 

 

아래 링크도 참고하세요.

stackoverflow.com/questions/43759231/sending-and-receiving-all-data-c-sockets

 

Sending and receiving all data (C++ sockets)

Help me please to find out what is wrong in client-server communication... The client sends to server jpeg frames from camera 25 times per second using this function: int SendAll(SOCKET &

stackoverflow.com

근데 이때, 구조체가 가변길이이고, 패킷 헤더에서 패킷의 길이를 전달해주는 구조라면 구현이 더욱 복잡해집니다. 

실제 이렇게 세세한 부분까지는 자료도 거의 없어서 직접 구현을 해야한다는 어려움이 있습니다. 

반응형
반응형

선언해야할 헤더는 2가지입니다.

 

#include <unistd.h> 
#include <fcntl.h> 

 

리눅스에서는 모든것을 파일형태로 관리합니다. 소켓또한 파일디스크립터 번호를 부여하고 관리합니다.

파일에 대한 속성을 변경하거나 얻어올 때 사용하는 함수가 바로 fnctl입니다.

 

 

int fcntl(int fd, int cmd, ... /* arg */ );

형태로 사용합니다.

반환값은 cmd에 따라 다르고 실패한다면 -1을 전달합니다.

 

아래는 커맨드입니다.

 

 

아래 코드는 논블록킹 구현을 위한 예제입니다.

 

 

위와같이 함수를 호출하고, 서버와 클라이언트에 대한 소켓이 생성될때 해당 함수를 호출해주면 되는 구조입니다.

반응형
반응형

위와 같은 형태를 가지고 있죠.

가변길이 구조체란 배열의 크기를 동적으로 할당하여 사용하는 것이다.

 

 

이때 2가지를 지켜야한다.

 

  • 1. 사이즈가 0인 array는 가장 아래에 선언해야한다.
  • 2. 동적할당을 하고 사용해야 한다.

동적할당하는 방법이나, 세부적인 내용은 아래 링크들을 참고하시면 감이 올거다.

 

 

그리고 논란인 char name[0] char name[]의 차이점은 

즉, 같은 문법으로 사용되고 있고, 표준은 []이 정확하다는 의견이다. c99 이후 버전을 사용한다면 []를 지향하자.

 

 

 

 

 

computersource.tistory.com/61blog.naver.com/PostView.nhn?blogId=sdi760210&logNo=70084541983&parentCategoryNo=63&viewDate=&currentPage=1&listtype=0

 

가변길이 구조체(flexible array member)

커널 코드를 살펴보다, 아래 예제와 같이 struct 선언 마지막에 size가 0인 array를 선언하는 코드를 발견하였다. 1 2 3 4 5 6 7 struct account {     int age;     int gender;     int name_..

computersource.tistory.com

blog.naver.com/PostView.nhn?blogId=sdi760210&logNo=70084541983&parentCategoryNo=63&viewDate=&currentPage=1&listtype=0

 

가변길이 구조체 벰버(Flexible Array Member )

--- 읽기전 당부사항 --- 나름 자료를 찾고 이해해서 올린 자료입니다. 혹시나 틀리게 있다면 뭐 이런게 다...

blog.naver.com

 

www.forbes.com/sites/quora/2013/05/14/what-is-the-advantage-of-using-zero-length-arrays-in-c/?sh=660c8a2f213a

반응형
반응형

네트워크 소켓통신시에는 패킹이라는 것을 해야합니다.

 

우선, 왜써야 할까?

 

 

해당 구조체에서 Data 구조체의 크기는?    data[0]는 단순히 메모리 위치만은 가르키는 녀석이기 때문에 4+ 1+ 1+ 1 = 7 이어야 하지만, 8이 나오는 모습을 볼 수 있었다.

 

그 이유는 

 

바이트 패딩: 

몇몇 컴파일러는 구조체의 필드를 메모리에 위치시킬때 중간에 빈공간없이 쭉 이어서 할당하는 경우도 있지만 대부분의 컴파일러는 성능향상을 위해 CPU가 접근하기 쉬운 위치에 필드를 배치하는데(OS나 컴파일러에 따라 4바이트단위 또는 8바이트 단위로 끊는작업을 함) 이를 "바이트 패딩"이라고 하며, 그러다보니 중간이 빈 공간이 들어가게 되는데 이 빈 공간을 "패딩비트"라 한다.  

 

이녀석 때문이다.

 

패딩 비트를 제거해주고, 연속된 메모리에 데이터를 할당하고 소켓통신을 해야만 올바르게 데이터를 주고 받을 수 있다.

 

그렇기 때문에 소켓통신시에 패킹이 필요한 것이다.

 

사용방법은 간단!!

 

이런식으로 데이터를 묶어주면 된다.

헤더는 #include <stdio.h>

 

pack(push, 1) // 1바이트 크기로 정렬하겠다는 의미

pack(pop) // 정렬 설정을 이전 상태(기본값)로 되돌리겠다는 의미

 

해당방법은

  • Visual Studio, GCC 4.0 이상

의 환경에서만 가능하다.

 

이전버전을 사용한다면( 그럴 일은 거의 없지만..) 혹은 다른 방법을 원한다면 아래 링크들을 참고

 

dojang.io/mod/page/view.php?id=432

 

C 언어 코딩 도장: 51.2 구조체 정렬 크기 조절하기

데이터 전송이나 저장을 할 때 구조체 정렬을 피하려면 어떻게 해야 할까요? 그런데 C 언어에서는 구조체를 정렬하는 표준 방법이 없습니다. 하지만 걱정하지 않아도 됩니다. 각 컴파일러에서

dojang.io

 

www.vbflash.net/164

https://3dmpengines.tistory.com/1571 [3DMP]

 

C/C++ 및 C# Scoket 통신

Introduction 본 블로그의 포스팅과 관련된 내용을 작성한지 몇년이 지났는데, 에버노트를 정리하다가 버릴까 하던 내용을 다시 다시 주워서 정리 후 포스팅합니다.  최근에는 C/C++을 이용한 개발

www.vbflash.net

 

반응형
반응형

우선순위 큐는 일반 큐와는 다르게  트리구조입니다.

그리고 현재 저장된 값들 중 최댓값 혹은 최솟값을 내놓습니다.

그렇기 떄문에 삽입, 삭제 할 때 시간복잡도는 O(logN) 입니다.

 

<사용 함수>
push(X) // 우선순위 큐에 새로운 값을 추가

top() // 우선순위 큐의 루트 노드 리턴

pop() // 우선순위 큐에서 루트 노드를 삭제

size() // 우선순위 큐 내에 포함된 원소들의 수를 리턴

empty() // 우선순위 큐가 empty이면 true를 리턴

 

우선순위 큐 중 최댓값을 내놓는 것을 Max 힙, 최솟값을 내놓는 것을 Min 힙이라고 부릅니다.

 

Max 힙을 사용

 

1. priority_queue< int, vector<int> > q;
2. priority_queue< int, vector<int>, less<int> > q; 

* less 의미가 Max 힙의 의미와 헷갈릴 수 있으니 주의

3. 전달할 데이터가 2개 이상이면 다음과 같이 pair로 묶는다.

priority_queue< pair<int,int> > pq;

 

Min 힙을 사용


1. priority_queue< int, vector<int>, greater<int> > q;

2. 혹은 priority_queue< int, vector<int> > q;를 쓰고 push(-X) & -top() 같이 사용 가능.

3. 전달할 데이터가 2개 이상이면 다음과 같이 pair로 묶는다.

priority_queue< pair<int,int> > pq;를 쓰고 push(-X) & -top() 같이 사용 가능.

 

pair로 묶었다면 .first 값을 1순위로 값을 뽑아내고 그다음 .second 값을 2순위

pair< int,pair<int,int> > 형태는 .first 1순위 .second.first 2순위 .second.second 3순위

 

그리고 sort함수와 동일하게 사용자가 직접 함수를 생성해서 우선순위를 구분해줄 수도 있습니다.

그건 다른 포스팅에서 올리도록 하겠습니다. 




반응형
반응형

sort함수에서 해당 오류가 뜬다면 작성하신 비교함수에 문제가 있는겁니다.

 

아마 아래처럼 = 부호를 포함해서 작성하셨을 겁니다.

 

bool cmp(int a, int b) {
	if (a >= b)
		return true;
	return false;
}

 

sort를 위한 compare 함수는 반드시 strict weak ordering을 만족해야 합니다. 즉, a==b인 경우에는 a<b도 false이고 a>b도 false가 되어야 합니다.

 

몰랐던 사실하나 알아가네요.

반응형

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

c, c++ 구조체 패킹  (0) 2021.03.10
c++ 우선순위 큐 priority_queue 사용법  (0) 2020.07.29
C++ memset 사용법  (0) 2020.05.03
C++ upper_bound, lower_bound 사용하기  (0) 2020.03.21
C++ math사용법 총정리  (0) 2020.03.13
반응형

라이브러리 -> #include <cstring> (memset 함수는 기존 c언어의 string에 들어있기 때문에 cstring을 선언)

 

void * memset ( void * ptr, int value, size_t num );

첫번째 인자는 배열의 시작주소, 두번째는 초기화할 값, 세번째는 배열의 크기입니다.

 

 

사용예시 

int arr[10][10]
memset(arr, 0, sizeof(arr));
반응형

+ Recent posts