캡슐화 Encapsulation
접근 지정자 Access Specifiers 
접근함수Access Functions

큰프로그램을 개발하게 되면 내용이 복잡해진다.
많은변수,많은함수,많은클래스가 사용된다.

뛰어난 프로그래머는 복잡해보이는걸 단순하게 정리잘한다.
재사용을 강조한다.

접근 지정자 Access Specifiers 
public 밖에서 접근이 가능하게한다.
private 밖에서 접근하는걸 막아준다.(기본으로 지정)

private으로 접근지정자가 지정된 클래스는 access Functions(접근함수)를 만들어 주어야한다.
클래스 내부안에 access Functions는 private 변수나 함수에 접근 가능하다.

class cDate
{
//private 밖에서 접근이 불가능하다.
private:	//access specifier(접근지정자)
	int m_month;
	int m_day;
	int m_year;
//access functions 생성
public:
	void setDate(const int &m, const int &d, const int &y)
	{
		m_month = m;
		m_day = d;
		m_year = y;
	}
}

같은 클래스로 생성된 인스턴스는 같은 클래스의 인스턴스에 접근이 가능하다.

class cDate
{
private:	//access specifier(접근지정자)
	int m_month;
	int m_day;
	int m_year;
//access functions 생성
public:
	void copyFrom(const cDate& original) {
		//original안에 변수들은 private이다.
		//같은 클래스 변수접근
		m_month = original.m_month;
		m_day = original.m_day;
		m_year = original.m_year;
	}
};


캡슐화장점
public로 생성 밖에서 변수에 접근해 작업하면 클래스안에 변수가 변경되면 변경해야할 코드가 엄청많아진다.

 

#include <iostream>

using namespace std;


struct sDate
{
	int m_month;
	int m_day;
	int m_year;
};

class cDate
{
private:	//access specifier(접근지정자)
	int m_month;
	int m_day;
	int m_year;
//access functions 생성
public:
	void setDate(const int &m, const int &d, const int &y)
	{
		m_month = m;
		m_day = d;
		m_year = y;
	}

	const int& getMonth() {
		return m_month;
	}

	const int& getDay() {
		return m_day;
	}

	const int& getYear() {
		return m_year;
	}

	void copyFrom(const cDate& original) {
		//original안에 변수들은 private이다.
		//같은 클래스 변수접근
		m_month = original.m_month;
		m_day = original.m_day;
		m_year = original.m_year;
	}
};

int main()
{
	//struct는 바로접근이 가능하다.
	sDate toDay{ 8,4,2025 };
	toDay.m_month = 8;
	toDay.m_day = 4;
	toDay.m_year = 2025;

	//클래스는 바로 접근할 수 없다.
	//접근하려면 public 접근지정자를 선언해주어야한다.
	cDate cToDay;
	cToDay.setDate(8, 4, 2025);
	cToDay.getMonth();

	cDate copy;
	copy.copyFrom(cToDay);

	return 0;
}

'개발 소발 > 개발 C++(기초)' 카테고리의 다른 글

c++ 생성자멤버초기화,위임생성자  (0) 2019.08.05
c++ 생성자 Constructor  (0) 2019.08.05
c++ OOP 객체지향 프로그래밍  (0) 2019.08.02
c++ assert,Ellpsis  (0) 2019.08.02
c++ 방어적프로그래밍 기초  (0) 2019.08.02

우리가 사용하는 웹브라우저는 URL을 입력하면 DNS에서 IP를 받아와

IP에 리퀘스트request를 만들어 요청한다.

리퀘스트request를 요청 받은 WEB서버는  처리 후 response를 반환해준다.

 

웹브라우저에서 웹에 접속할때 간단히 표현하면

클라이언트 요청

-> 리퀘스트 생성

-> 리퀘스트 전달

-> 웹서버에 통신도착

-> 리퀘스트 해독

-> 리스폰스 반환

통신과정을 거친다.

 

그런데 이런 통신을 할려면 모두가 사용하는 약속이 필요하다.

그 약속 = 프로토콜이 HTTP이다.

 

HTTP1.1이 된후 WEB 뿐만 아닌 다양한 곳에서 HTTP 프로토콜이 사용되고 있다.

 

위에서 말한 HTTP는 TCP/IP 프로토콜(약속)중 하나를 말한다.

 

여기서말한 TCP/IP 프로토콜은 TCP,IP프로토콜 2개를 말하는게 아닌

IP프로토콜을 사용하는 통신에 사용되는 프로토콜을 총칭해 TCP/IP라고 표현한다.

 

TCP/IP에는 계층이라는 중요한 개념이 있다.

계층은 애플리케이션,트랜스포트(TCP),네트워크(IP),링트 4계층으로 이루어져 있다.

 

간단히 알아보면

애플리케이션(HTTP): 리퀘스트request를 작성한다.

트랜스포트(TCP):  TCP 부분이다.작성된 데이터를 통신하기 쉽게 분해하고 안내,포트번호를 붙인다.

네트워크(IP):트랜스포트 계층에서 분해된 데이터에 수신될 MAC주소를 추가해 링크계층에 전달한다.

링크:하드웨어를 제어하는 디바이스드라이버,인터페이스카드, 하드웨어 케이블 등을 말한다.

링크를 통해 데이터가 전달된다.

 

송신할땐 애플리케이션 -> 트랜스포트 - > 네트워크 순으로 캡슐화하고

반대로 수신측에선

네트워크 -> 트랜스포트 -> 애플리케이션 순으로 캡슐화를 제거한다.

Object Oriented Progamming

OOP 객체지향 프로그래밍

객체는 데이터와 기능이 묶여있는 개념이다.

예를 들어

친구들의 정보를 출력 프로그램을 작성한다고 생각해보자.
한명이아닌 여러명의 이름,주소,나이등을 출력해야한다.

데이터를 묶어서 구조체 struct를 생성하는 방법이 있다.

구조체안에 기능을 구현하는건 추천하지 않는다.

class는 Object를 문법을 구현할때 사용한다고 생각하면된다.
구조체보다 class는 많은 기능을 사용할 수 있다.

Class도 변수처럼 선언해주어야한다.
Object 메모리를 차지하도록 정의해주는것을 instanciation이라 한다.
메모리에 할당된 class를 instance라고 한다.

class는 접근지정자access specifier를 지정해주어야한다.
접근지정자는 public,private,protected 세가지가 있다.

구조체struct와 class의 큰 차이점
구조체는 접근지정자가 없다.

#include <iostream>
#include <string>
#include <vector>

using namespace std;

struct Friend{
	//멤버
	string m_name;
	string address;
	int age;

	void printFriend() {
		cout << "name : " << m_name << " addr : " << address << " age : " << age << endl;
	}
};

class ClassFriend{
//접근제한자
public:
	//멤버
	string name;
	string address;
	int age;

	void printFriend() {
		cout << "name : " << name << " addr : " << address << " age : " << age << endl;
	}
};

//함수를 따로 만들어 각변수 파라미터로 입력하기
void printFriend(const string &name, const string &address, const int &age) {
	cout << "name : " << name << " addr : " << address << " age : " << age << endl;
}

//함수를 따로 만들어 구조체를 파라미터로 입력하기
void printFriend(Friend f) {
	cout << "name : " << f.m_name << " addr : " << f.address << " age : " << f.age << endl;
}

int main()
{
	Friend f1;
	f1.m_name = "CHOI";
	f1.address = "test";
	f1.age = 100;
	
	//파라미터로 다 직접 넣기는 귀찮다.
	printFriend(f1.m_name, f1.address, f1.age);

	//구조체로 더 간단하게 코드를 작성한다.
	Friend f2{ "KIM","TEST",133 };
	//구조체를 파라미터로 사용한다.
	printFriend(f2);

	//구조체 struct안에 print를 선언한다.
	f2.printFriend();

	//클래스로 생성하기
	ClassFriend f3{ "KIM","TEST",133 };
	f3.printFriend();

	//vector를 클래스로 선언해 쉽게 활용할 수 있다.
	vector<ClassFriend> friendVec;
	friendVec.push_back(f3);
	friendVec.push_back(ClassFriend{ "KIM1","TEST1",1 });
	for (int i = 0; i < friendVec.size(); i++) {
		friendVec[i].printFriend();
	}

	return 0;
}

'개발 소발 > 개발 C++(기초)' 카테고리의 다른 글

c++ 생성자 Constructor  (0) 2019.08.05
c++ 캡슐화,접근지정자,접근함수  (0) 2019.08.05
c++ assert,Ellpsis  (0) 2019.08.02
c++ 방어적프로그래밍 기초  (0) 2019.08.02
c++ 재귀적 함수호출  (0) 2019.08.01

프로그래밍은 디버깅이 시간이 오래걸린다.
assert은 디버깅을 컴파일러에게 도움받는 방법이다.

디버그 모드에서만 작동한다.
assert(false)는 에러가 출력된다.
assert(조건) 방식으로 작성한다. 조건에 맞지않으면 오류내용을 출력해준다.

static_assert는 컴파일타임에 결정되는 변수만 사용가능하다.
또, 오류내용 입력이 가능하다.

#include <iostream>
#include <cassert>
#include <array>

using namespace std;

void getArrData(array<int, 5> &arr, const int &idx) {
	//콘솔창에 오류내용이 출력된다.
	assert(idx >= 0);
	assert(idx <= arr.size() - 1);

	cout << arr[idx] << endl;
}

int main() {

	const int num = 5;
	//반드시 맞아야하는 데이터를 입력한다.
	assert(num == 5);
	//static_assert는 컴파일타임에 결정되는 변수만 저장가능하다.
	static_assert(num == 5,"GOOD");

	array<int, 5> arr{ 1,2,3,4,5 };
	getArrData(arr, 100);

	return 0;
}

 

함수를 구현하다보면 파라미터가 정해져있지 않으면 좋겠다 싶을때가있다.
이때 Ellipsis를 사용한다.

 

주의할점
파라미터 갯수를 맞춰주어야한다.
타입도 미리 정해주어야한다.

사용하기가 위험하고 디버깅도 어렵다.

고수가 되면 사용하도록 하자!

#include <iostream>
#include <cstdarg>

using namespace std;

double findAverage(int count, ...) {
	double sum = 0;
	//파라미터로 꺼내올 리스트를 선언한다.
	va_list list;

	//리스트와 크기를 정해준다.
	va_start(list, count);

	for (int arg = 0; arg < count; arg++) {
		//리스트안에서 값을 꺼내온다.
		//intiger로 변환
		sum += va_arg(list, int);
	}
	//리스트를 마무리해준다.
	va_end(list);

	return sum / count;
}

int main()
{
	cout << findAverage(3, 1, 2, 3) << endl;
	//사용자가 지정한 카운트와 크기가 안맞으면 문제가 생긴다.
	cout << findAverage(100, 1, 2, 3) << endl;
	return 0;
}

다수가 사용하는 프로그램을 작성할땐 사용자가 어찌 사용하든 정상적으로 작동하는게 좋다.

문법오류 syntax error
- 코드를 잘못 작성했을때를 말한다.

문맥오류 semantic error
- 논리오류:프로그래밍 로직 자체가 틀렸을때를 말한다.


가정위반 violated assumption
- 사용자가 개발자가 의도한 행위가 아니게행동하는 경우를 말한다.

방어적으로 프로그래밍하는 이유
작성한 프로그램이 에러가 방지차원.
사용자를 믿는게 아닌 개발자가 미리 에러를 방지한다.
방어적프로그래밍은 시간이 오래걸리지만 꼭해주어야한다.

 

#include <iostream>
#include <string>

using namespace std;
int a = 0;
int main()
{
	string s = "test test test";
	while (true)
	{
		//사용자에게 범위를 말한다.
		cout << "input from 0 to " << s.size() - 1 << endl;
		int x;
		cin >> x;
		//사용자에게 범위를 말하는것만 아니라 방어까지 해놓는다.
		if (x <= s.size() - 1) {
			cout << "GOOD! " << s[x] << endl;
			break;
		}
		else {
			cout << "again" << endl;
		}

	}

	return 0;
}

재귀적함수호출 Recursive Function Call

함수가 다시 자기자신 함수를 호출하는것

함수도 메모리에 올라가는것이기에 주소에 접근해 계속 호출이 가능하다.
종료하는 조건을 반드시 만들어야한다.
재귀적함수호출를 너무 많이하면 Stack Ove Flow가 발생한다.

#include <iostream>
#include <vector>

using namespace std;

void countDown(int count) {
	cout << count << endl;
	if (count != 0) {
		countDown(count - 1);
	}
}
int a = 1;

int sumTo(int sumto) {
	
	if (sumto <= 0) {
		return 0;
	}
	else if (sumto <= 1) {
		return 1;
	}	
	else {
		int sum_minus_one = 0;
		sum_minus_one =	sumTo(sumto - 1);
		return sum_minus_one + sumto;
	}
}

int main()
{
	countDown(10);
	cout << "=========================" << endl;
	cout << sumTo << endl;
	//재귀호출하면 10에서 1씩 감소시키며 1이 나올때까지 반복한다.
	//1이 나오면 리턴값으로 하나씩 돌려준다.
	//1(else if(sumto <= 1)의 결과값)+2(sumto)
	//3은 그전에 호출된 sum_minus_one의 결과값으로 간다.
	//3 + 3, 6 + 4, 10 + 5 형식으로 계속 돌려준다.
	//cout << sumTo(10) << endl;
	cout << test(0) << endl;

	vector<int> v;
	v.reserve(10);
	v.push_back(45+10);
	v.push_back(36+9);
	v.push_back(28+8);
	v.push_back(21+7);
	v.push_back(15+6);
	v.push_back(10+5);
	v.push_back(6+4);//return 6을 받고 동작
	v.push_back(3+3);//return 3을 받고 동작
	v.push_back(1+2);//return 1을 받고 동작


	return 0;
}

동적할당메모리를 직접관리하는것보다 vector를 사용하는게 더 쉽다.

동적할당을 할때 new,delete하는게 오래걸린다.
new와 delete를 최소화하는것이 좋다.

vector는 더 작은쪽으로 resize를 하면 메모리를 반납하지않고 크기만 줄여서 보여준다.
size,capacity로 알아볼 수 있다.


reserve로 메모리의 용량을 미리 확보할 수 있다.
용량을 확보하고 출력해보면 확보된 메모리가 다 출력되지 않는다.
사용하는이유 : 메모리를 미리확보해서 new,delete를 줄인다.

#include <iostream>
#include <vector>

using namespace std;

int main()
{
	vector<int> v{ 1,2,3 };
	//size,capacity
	//capacity 는 실제로 메모리에 가지고 있는 갯수
	//size는 메모리에서 현재 사용하는 갯수
	cout << "=====================================" << endl;
	v.resize(2);
	for (int &a : v) {
		cout << a << endl;
	}
    //2가 출력된다.
	cout << v.size() << endl;
    //3이 출력된다.
	cout << v.capacity() << endl;

	//강제로 포인터로 변경해서 데이터가져오기
	//데이터가 남아있는걸 확인할 수 있다.
	int *ptr = v.data();
	cout << ptr[2] << endl;
	cout << "=====================================" << endl;
	v.reserve(1024);
	cout << v.capacity() << endl;
	//사용하고있는 데이터 1,2만 출력된다.
	for (int &a : v) {
		cout << a << endl;
	}
	cout << "=====================================" << endl;
	
	return 0;
}


vector를 stack처럼 사용하기
스택을 메모리에 쌓는다고 생각하면된다.
vector에 추가할때push를 제거할때 pop을 사용한다.

사용하는이유 reserve로 크기를 설정해놓으면 설정한 크기까진
new,delete를 계속 할 필요가 없다
reserve를 너무크게 설정하면 메모리가 낭비된다.

#include <iostream>
#include <vector>

using namespace std;

void printStack(const vector<int> &stack) {
	cout << "printStack" << endl;
	for (auto &a : stack) {
		cout << a << endl;
	}
	cout << "end" << endl;
}
int main()
{
	//push_back은 추가한다.
    //pop_back은 제거한다.
	vector<int> stack;
    //미리 메모리를 확보한다.
	stack.reserve(1024);
	stack.push_back(3);
	printStack(stack);
	stack.push_back(5);
	printStack(stack);
	stack.push_back(7);
	printStack(stack);
	stack.pop_back();
	printStack(stack);
	stack.pop_back();
	printStack(stack);
	stack.pop_back();
	printStack(stack);

	return 0;
}

'개발 소발 > 개발 C++(기초)' 카테고리의 다른 글

c++ 방어적프로그래밍 기초  (0) 2019.08.02
c++ 재귀적 함수호출  (0) 2019.08.01
c++ 스택Stack 힙Heap  (0) 2019.07.31
c++ 함수포인터 Function Pointers  (0) 2019.07.31
c++ 함수 오버로딩  (0) 2019.07.31

CPU와 메모리의 사용알기
스택Stack
힙 Heap

프로그램을 실행하면 운영체제는 메모리를 할당해준다.
메모리는 여러 구역으로 나뉜다.
코드 
-작성한 프로그램 메모리에 올라가있다. 
BSS
-0으로 초기화된 전역,정적변수가 저장된다.
DATA
-전역,정적변수가 저장된다.
스택
-Stack frame으로 순서대로 쌓인다.
-스택은 비교적 빠르다.
-스택은 빠르나 비교적 크기가 작다.

-동적메모리가 할당되면 힙에 저장된다.
-메모리 어느부분에 쌓이는지 알 수 없다.
-할당된메모리가 반납안되면 계속 쌓여 메모리 누수가 생긴다.

 

스택 알아보기

아래 소스에서 실행되는 순서대로 스택에 쌓이게된다.

 

1.Stack frame main(),int a,b

2.Stack frame first(),int x,y

3.Stack frame second(),int x

#include <iostream>

//BSS 파트 처음에 생겼다가 마지막에 사라진다.
int g_i = 0;

//3.Stack frame second(),int x
int second(int x) {
	return 2 * x;
}

//2.Stack frame first(),int x,y
int first(int x) {
	int y = 3;
	return second(x + y);
}

//1.Stack frame main(),int a,b
int main()
{
	using namespace std;
	int a = 1, b;
	b = first(a);
	cout << b << endl;

	return 0;
}

힙 알아보기

#include <iostream>

void initArray() {
	//메모리를 반납하지않으면 사용은 못하는데 메모리엔 할당되어있다.
	int *ptr2 = new int[1000];
}

int main()
{
	int *ptr = nullptr;
	//힙메모리에 동적할당된 주소를 가져온다.
	ptr = new int[1000000];
	//힙메모리에 할당된 메모리를 반납한다.
	delete[] ptr;
	//메모리반납해도 주소는 가지고있다 초기화해준다.
	ptr = nullptr;

	//메모리 누수 예
	initArray();

	return 0;
}

함수포인터Function Pointers

함수도 포인터다. 

즉, 함수도 메모리 주소를 가지고 있다.
함수를 실행하면 함수의 메모리주소를 찾아 실행한다.

int (*포인터명)(파라미터) = 함수명; 형식으로 생성할 수 있다.

함수포인터를 파라미터로 넣을 수 있다.

반복되는 로직이 있으면 포인터를 통해 다 분리해서 사용하는게 가능하다.


함수파라미터도 기본값이 설정가능하다.
함수는 이름 자체가 포인터라 &를 안넣어도 된다.

함수가 파라미터로 자주사용되면 계속쓰기 귀찮으면 typedef로 선언해서 사용할 수 있다.
#include <functional>을 사용해 더 간단히 사용할 수 있다.

 

#include <iostream>
#include <array>
#include <functional>

using namespace std;

int func() {
	return 5;
}

void printNumbers(const array<int, 10>& myArr,bool print_even) {
	for (int a : myArr) 
	{
		if (print_even && a % 2 == 0) {
			cout << a << " ";
		}
		else if (!print_even && a % 2 == 1) {
			cout << a << " ";
		}
	}
	cout << endl;
}

bool isEven(const int& number) {
	
	if (number % 2 == 0) return true;
	else return false;
}
bool isOdd(const int& number) {
	if (number % 2 != 0) return true;
	else return false;
}

void printNumbersFunP(const array<int, 10>& myArr, bool (*check_fcn)(const int&)) {
	for (int a : myArr)
	{
		if (check_fcn(a)==true) {
			cout << a << " ";
		}
	}
	cout << endl;
}

void printNumbersFun(const array<int, 10>& myArr, function<bool(const int&)> funct) {
	for (int a : myArr)
	{
		if (funct(a) == true) {
			cout << a << " ";
		}
	}
	cout << endl;
}

int main()
{
	//함수의 주소가 출력된다.
	cout << func << endl;
	
	//함수포인터 설정하기
	int (*funcPtr)() = func;

	//func()함수와 메모리 주소도 같고 같이 사용한다.
	cout << funcPtr << " " << funcPtr() << " " << &funcPtr << endl;

	//함수포인터 사용하기전 홀수 짝수 출력 함수
	std::array<int, 10> myArr{ 1,2,3,4,5,6,7,8,9,10 };
	printNumbers(myArr, true);
	printNumbers(myArr, false);

	//함수포인터 파라미터로 사용하기(함수명)
	printNumbersFunP(myArr, isEven);
	printNumbersFunP(myArr, isOdd);

	std::function<bool(const int&)> funct = isEven;
	//fuction사용해서 함수포인터 파라미터로 이용하기(포인터명)
	printNumbersFun(myArr, funct);
	funct = isOdd;
	printNumbersFun(myArr, funct);

	return 0;
}

함수오버로딩 Fuction Overloading

동일한 이름의 함수를 여러개 만드는 것을 말한다.

 

매개변수 = parameter


일반적으로 들어오는 매개변수가 다른데 수행하는기능이 비슷한경우 사용한다.
전혀다른 기능을 수행할 수도 있긴하다.
반환형태가 달라도 이름이 같고 매개변수도 같으면 에러가 난다.
즉, 매개변수가 다르면 다른 함수로 판단한다.

매개변수가 없이 오버로딩하는 법은 반환값을 void로 선언하고 참조변수로 받는 방법이 있다.
단, 리턴받지않고 변수를 선언하고 인자로 넣어야하는 단점이 있다.

void add(int &a) {
	a = 30;
}


컴파일할때 어떤 함수를 사용할지 결정되어야한다.
또 인자를 정확히 표현해주어야한다.

	//double형
    cout << typeid(add(1.3, 1.5)).name() << " " << add(1.3, 1.5) << endl;
	//float 형
    cout << typeid(add(1.3f, 1.5f)).name() << " " << add(1.3, 1.5) << endl;

오버로딩 예

#include <iostream>

using namespace std;

void add(int &a) {
	a = 30;
}

int add(int x, int y) {
	return x + y;
}

int add(unsigned int x, unsigned int y) {
	return x + y;
}
double add(double x, double y) {
	return x + y;
}

float add(float x, float y) {
	return x + y;
}

int main()
{
	cout << typeid(add(1, 3)).name() << " " << add(1, 3) << endl;
	cout << typeid(add(1u, 3u)).name() << " " << add(1, 3) << endl;
	cout << typeid(add(1.3, 1.5)).name() << " " << add(1.3, 1.5) << endl;
	cout << typeid(add(1.3f, 1.5f)).name() << " " << add(1.3, 1.5) << endl;

	cout  << (unsigned int)'a' << endl;

	return 0;
}

+ Recent posts