cpp01

YP J·2022년 6월 10일
0

CppModule

목록 보기
2/9

배낀곳
https://velog.io/@dogfootbirdfoot/CPP-Module-01
https://velog.io/@zhy2on/CPP-01
(private)https://www.notion.so/donpark/CPP01-0d66d64924654e6d87385b18f606e539

rules

  • 헤더에 함수가 구현되거나, 헤더가 보호되지 않으면 과제는 0점 처리.
  • 모든 출력은 표준 출력으로 전달되어야 하며, 서브젝트에 명시되지 않은 이상 개행으로 끝나야 한다.
  • 더이상 c가 아닌 최대한 c++ 버전을 찾아 응용하는 것이 현명하다.
  • STL 사용가능한 과제가 나오기 전까지는 금지.
  • using namespace, friend 는 금지
  • ClassName.hpp, ClassName.cpp로 작성
  • 디렉토리는 ex00, ex01, ... 형식
  • c++98버전으로 작성
  • c++ 컴파일러를 사용
  • 코딩 스타일은 자유. 단 평가자가 이해할 수 없는 코드는 점수받기 힘들다.
  • 기계채점이 없다. 단 과제에서 요구하는 사항들을 놓치지 않도록 주의해서 할것.
  • 제출 파일 중에는 관련없는 파일이 섞여있어도 무방. 코드를 여러 파일로 분리하여 요구사항보다 더 많은 양의 파일을 제출해도 좋습니다.
Turn-in directory : ex00/
Files to turn in : Makefile, main.cpp, Zombie.{h, hpp}, Zombie.cpp,
newZombie.cpp, randomChump.cpp
Forbidden functions : None

ex00(BraiiiiiinnnzzzZ)

단일 객체 생성
1.void announce(void),
2.Zombie* newZomibe(std::string name),
3.void randomChump(std::string name)

이 함수들을 구현하고 사용 방식에 따라 스택영역 힙역역에 할당하고 적절한때 소멸해야한다.

Zombie 함수는 New 연산자를 사용해서 동적할당해서 Zombie를 생성 하는것으로 구현

randomChump는 동적할당 하지 않고 Zombie를 생성 하는 함수로 구현

Zombie객체를 클래스의 외부에서 생성하는 경우 항상 new연산자를 사용해서 도적으로 생성 해야만 생성할 수 있도록 해두었고,동적할당 하지 않는 경우는 클래스 내부에서 생성하고 사라질 수 있도록 설계

  • 이렇게 하기 위해서 생성자를 private 접근 지정자의 위치에 두므로써 구현 할수 있다.
  • default 생성자는 생성자가 하나라도 선언 되어 있다면 없는 취급되므로 클래스 내부에서만 생성자를 호출할 수 있게 된다.

이렇게 하면 Zombie 객체를 동적할당 하지 않을 경우 특정 함수가 종료 되면서 자동 소멸된다.
동적 할당한 경우는 직접 delete로 삭제 해줘야 한다.( delete 실행시 자동으로 소멸자 호출됨, 동적할당 안 할경우는 함수가 끝날때 자동으로 소멸자 호출됨)

그리고 추가적으로 randomChump 에서 생성되는 Zombie는 random한 6 글자 이름을 가질수 있도록 구현.?


keyword : new $ delete

  • new 가 malloc 역할 , 다른점은 new 생성자를 호출한다는것.
    malloc으로 인스턴스 할당하면 생성자가 호출되지 않아서 쓰레기 값이 들어가버림.
    new 키워드 자체가 malloc 과 생성자 호출을 랩핑한 형태.
  • new키워드로 힙에 메모리를 할당 해 줬으면 delete로 해제해줘야함.
  • delete 는 메모리 해제와 소멸자 호출이 랩핑한 형태?

객체(인스턴스) 를 생성하는 두 가지 방법:

stack 영역

  • 런타임 시점에서 크기가 결정된다.
  • LIFO(Last IN First OUT)
  • 함수의 매개변수나 지역변수가 위치한곳.
  • 스택의 경우 선언했던 함수 영역을 벗어난 경우 자동으로 소멸된다.
  • . 연산자를 통해서 인스턴스에 접근 가능
Zombie stack = Zombie("Zombie_in_stack"); // stack 할당 예시

heap 영역

  • 컴파일 시점에서 크기가 결정된다.
  • 동적 메모리 할당(new)를 통해서 저장되는 영역.
  • 힙 영역은 프로세스에서 동적으로 로드된 모듈에서 공유 된다.
  • -> 연산자를 통해 인스턴스에 접근할 수 있다.
  • 사용이 종료된 시점에 할당을 해제(delete) 하지 않으면 메모리 누수가 발생.
Zombie *heap = new Zombie("Zomibe_in_heap") // heap 예시



ex01

Turn-in directory : ex01/ ;
Files to turn in : Makefile, main.cpp, Zombie.{h, hpp}, Zombie.cpp,
zombieHorde.cpp
Forbidden functions : None

많은 좀비를 한번에 할당 하고 첫번째 생성한 Zombie 의 주소값을 반환하기 위해서 배열을 사용
객체를 N게 저장할수 있는 배열 생성법 익히기!

객체 배열 생성시 생성자에 인자를 넘길수 없고, 디폴트 생성자만 호출된다.

방법 1.

Zombie arr[N];

방법2.(동적할당)

Zombie *arr = new Zombie[N];

new[] & delete[]

  • new 로 배열 할당하고 싶을 때 위의 방법2
  • 대신 new로 할당한 배열을 해제할때는 delete[] arr 이렇게 해줘야함

ex01 에선 N 만큼의 객체를 한 번에 할당해야하기 때문에 객체 배열을 선언해야 한다.
위에서 언급했듯이 객체 배열 생성시 생성자에 인자를 넘길수 없고, 디폴트 생성자만 호출되므로-> 생성자를 통한 이름 초기화는 불 가능하기 때문에
디폴트 생성자가 필요함.

그리고 생성후 첫번째 객체의 주소를 반환하라고 했으므로 -> 객체 배열이 동적할당 되어야 한다.

new연산자를 이용해 한 번에 여러 공간을 할당받고 , 반복문을 이요해 각각의 객체에 이름을 부여하는것이다.

다만 클래스의 이름 변수는 private 접근제어자이기 때문에 외부에서 접근할수 없으므로
클래스 외부에서private 으로 선언된 변수를 변경하기위해서는 내부에 private변수에 접근할수 있는 public 함수를 만들어야 한다

이러한 함수를 setter 함수라 부른다.

변수를 public 으로 선언하지 않고 굳이 private으로 선언한뒤, setter 함수를 통해 접근하는 이유는?
-> 외부에서 무분별하게 변수의 값을 설정하는것을 막기 위해서, 함수를 통해서 접근한다면 예외처리가 가능하지만, 변수에 바로 접근할수 있다면 예외처리가 불가능하기 때문에.

객체 배열을 동적할당했기 때문에 사용후 반드시 delete 연산자 !!

ex02 (HI THIS IS BRAIN)

HI THIS IS BRAIN
Turn-in directory : ex02/
Files to turn in : Makefile, main.cpp
Forbidden functions : None

문자열 포인터와 문자열 참조자 를 비교해보는 문제
C에서는 변수 이름 앞에 &를 붙이면 변수의 주소 값 지칭을 의미하지만 C++ 에서는 타입에 대해서 &를 붙이게 되면 그 타입을 참조하는 참조자 선언을 의미한다.

pointer vs reference

  • https://www.geeksforgeeks.org/pointers-vs-references-cpp/
  • pointer는 변수의 메모리 주소를 담음. reference는 alias. 별칭이라고 생각하면 됨
  • pointer는 선언 후 값을 줄 수 있지만 reference는 선언과 초기화가 동시에 이루어져야 함. 선언 해놓고 나중에 값을 바꾸는 것 안 됨.

참조는 선언시 초기화 되어야 하며, NULL 값을 가질수 없다
포인터는 선언시 꼭 초기화 할 필요는 없으며 , NULL값을 가질수 있다.

std::string *strPTR = &str;
std::string &strREF = str;

stringPTR 은 str 의 주소값을 저장한다.
stringREF 는 str 의 또다른 이름으로 stringREF 를 선언 한것. (근데 얘를 변경하면 원래 것도 변경된다.)

참조자의 특성

  • 일종의 aliasing(별칭) 변수의 또 다른 이름이다.
  • 이름을 하나 더 두는 것 뿐 추가 메모리를 요구하지 않는다.
  • 포인터는 선언과 초기화를 별개로 해주어도 되는 것과 달리, 참조자에 대한 선언을 할 때는, 반드시 선언과 동시에 어떤 변수의 별칭이 될지 정해주어야 한다.
  • 포인터 변수는 재할당이 가능한 것과 달리, 참조자는 한 번 특정 변수에 대한 별칭이 되면 지칭하는 대상을 변경할 수 없다.
  • 대입 연산이 가능하긴 하지만 원래 참조하는 변수의 값이 바뀌는 것이지 참조 대상이 바뀌는 것은 아니다.
  • 함수의 인자로 참조자가 사용될 때는 추가 메모리 할당이 일어난다.

포인터 대신 참조자를 사용하는 이유

  • 포인터 변수(특정 변수의 주소 값이 저장되어 있는 변수)는 주소체계의 크기만큼 메모리에 할당하며 사용해야 하지만 참조자는 추가 메모리 할당 없이 별칭을 이용할 수 있다. (메모리 이용에 이점)
  • 참조자는 포인터처럼 *을 이용하지 않아도 되고 인자를 넘길 때도 &를 일일히 붙이지 낳아도 되기 때문에 더 가독성 좋게 이용할 수 있다.
  • 즉, 포인터의 경우 *나 -> 연산자를 통한 역참조를 해야 값을 가져올 수 있는 반면, 참조자는 일반 변수처럼 접근할 수 있다.
#include <iostream>
#include <string>

class Change {
public:
	static void change_normal(std::string str, char c) {
		str[0] = c;
	}

	static void change_ref(std::string &str, char c) {
		str[0] = c;
	}
};

int main(void)
{
	std::string str = "ABCDEF";
	std::string *stringPTR = &str;
	std::string &stringREF = str;

	Change::change_normal(str, 'x');
	std::cout << "normal)\tstr: " << str
		<< "\tstringREF: " << stringREF << std::endl;

	Change::change_ref(stringREF, 'y');
	std::cout << "ref1)\tstr: " << str
		<< "\tstringREF: " << stringREF << std::endl;

	Change::change_ref(str, 'z');
	std::cout << "ref2)\tstr: " << str
		<< "\tstringREF: " << stringREF << std::endl;

	return (0);
}

>>normal) str: ABCDEF	stringREF: ABCDEF
>>ref1)   str: yBCDEF	stringREF: yBCDEF
>>ref2)   str: zBCDEF	stringREF: zBCDEF
  1. normal) 에서는 값이 변경 되지 않았다.
  2. ref1) 에서는 값이 변경되었다.
  3. ref2) 에서는 값이 변경되었다.

ref1)에서 확인할 부분은 주소값을 넘긴게 아니지만 참조값이기 때문에 수정 가능하다.

ref2)에서 확인할 부분은 참조값을 매개변수로 받으면 참조한 변수인 stringREF만이 아니라 일반 str도 받을 수 있다.

ex03 (Unnecessary violence)

Turn-in directory : ex03/
Files to turn in : Makefile, main.cpp, Weapon.{h, hpp}, Weapon.cpp, HumanA.{h,
hpp}, HumanA.cpp, HumanB.{h, hpp}, HumanB.cpp
Forbidden functions : None

getter

  • getter : private 으로 멤버 변수를 선언하게 되면 그 변수를 직접적으로 손 대지 못하니, getter 라는 public 멤버함수를 만들어서 private 멤버 변수를 읽어오는것, 즉 가져오는것.

  • setter : 이건 값을 직접 바꾸는 용도.

함수 인자로서 참조자

  • 참조자를 함수 인자로 사용하게 되면 함수를 사용할때 &기호를 붙이지 않아도 알아서 참조자로 전달.

  • 이걸 함수안에서 다시 &를 써서 메모리 주소를 포인터 변수에 연결시켜 놓으면 함수 안에서 값을 업데이트 시킨것이 밖에서도 적용됨

  • 원래 C에선 포인터를 사용하지 않으면 무조건 변수가 복사 되어서 함수 안에서 지역변수로 사용이 되고 함수가 끝나면 사라져버렸음. 그래서 포인터 인자를 사용해서 애초에 함수에 &기호를 붙여서 메모리 주소로 전달해 줘야 했었는데

  • cpp 에선 그냥 함수 인자를 참조자로 선언해 놓으면 & 기호 안 붙이고 전달해 줘도 참조자로 전달.

  • 그대신 그걸 전달받을 멤버변수는 포인터로 선언해야함. 왜냐면 참조자는 선언과 동시에 초기화가 되어야하고, 업데이트 할수없기 때문


주어진 main.cpp

int main()
{
	{
		Weapon club = Weapon("crude spiked club");
		HumanA bob("Bob", club);
		bob.attack();
		club.setType("some other type of club");
		bob.attack();
	}
	{
		Weapon club = Weapon("crude spiked club");
		HumanB jim("Jim");
		jim.setWeapon(club);
		jim.attack();
		club.setType("some other type of club");
		jim.attack();
	}
	return 0;
}

포인터 vs 참조자

HumanA 와 HumanB 클래스를 정의할때, Weapon 을 참조자로 정의하는것 혹은 포인터로 정의하는것중 어느것이 바람직할까?

  • 문제에 따르면 HumanA 클래스는 항상 무장상태(객체 생성시 Weapon 클래스를 갖고 있는채로 생성) 이고

  • HumanB 클래스는 Weapon을 소지할 수도 있고 안 할수도 있다.

  • 주어진 main.cpp 를 보면 HumanA 는 생성시 Weapon 을 인자로 받기 때문에 포인터 혹은 참조자 중 어느것을 사용해도 되므로 참조자로 선언하는것이 적절하다

  • HumanA는 무조건 Weapon을 가지고 있는 상태여야하기 때문에 객체 생성시 무조건 초기화를 진행해야할 참조값으로 선언하는것이 좋다.

  • HumanB 는 Weapon을 인자로 받지 않기 때문에 Weapon 이라는 멤버 변수가 참조자일수 없다. (컴파일 에러)따라서 HumanB 의 Weapon은 포인터로 선성하는것이 적절하다.

  • HumanB 는 Weapon을 가질수도 있고, 없을수도 있기 때문에 Weapon 을 포인터로값으로 받아서 없는경우 NULL을 가지고 있도록 하게 한다.

참조자와 포인터에 대해 더 생각해보면

  • 포인터는 NULL도 표현할수 있기 때문에 포인터로 사용하는게 더 활용성이 높을 수 있지만
  • 상황에 따라 참조를 사용하는것도 코드의 안전성을 높여줄수 있다.
  • 여기서는 HumanA 가 꼭 무언가를 가지고 있어야 하는 상태에서 Weapon을 포인터로 두개 된다면 NULL이 들어 있을때 프로그램 오류로 볼수 있기 때문이다.

club 은 포인터 변수가 아니고 , HumanA의 생성자의 매개변수로 객체 자체를 받고 있으므로 HumanA의 생성자의 매개변수는 참조자임을 알 수있다. 또한 HumanB의 setWeapon 멤버 함수의 매개변수도 객체 자체를 받고 있으므로 HumanB의 setWeapon 멤버함수의 매개벼눗도 참조자임을 알수있다.

HumanA의 생성자의 매개변수는 참조자로 선언되었기 때문에 참조자의 특성상 선언과 동시에 초기화를 해주어야한다.
따라서 HumanA 생성자는 Member Initializer를 통해 참조자를 초기화 시켜주어야 한다.

Const 키워드

Weapon 클래스에 getType 이 상수참조를 반환하도록 해야한다고 되어있는데,
상수 참조를 반환하는 이유를 생각해보면 멤버 변수를 가져오긴 해야하지만 그 값을 변경할수는 없도록 하기 위함이다.

변수 선언시 const
  • 선언과 동시에 유효한 값으로 초기화 해야한다.
  • 초기화 이후 해당 변수의 값 변경 불가능
함수 인자가 const
  • 해당 인자는 함수 내에서 변경할수 없다
함수 뒤 const

ex) void example(int a) const
함수 뒤에 const 키워드가 붙으면 함수 안에서는 어떤 변수도 바꿀수 없음(mutable은 제외) 를 뜻한다.
함수가 클래스 멤버인 경우에만 const 키워드를 함수 뒤에 삽입할 수 있으며 해당 함수가 속한 객체의 맴버 변수를 변경할 수 없다는 뜻이다.

또한 const 가 붙은 함수 내에서는 const 가 붙은 다른 함수를 제외한 일반 함수는 호출하지 못한다.

이런한 기능을 가지고 있어 getter, bool 반환값에서 많이 사용되면 이로 인 해 함수 내부의 변수 변경을 방지할수 있다.

함수 반환값이 const
  • 해당 함수를 통해 반환받은 값은 변경할수 없다.

ex04 (Sed is for losers)

Sed is for losers
Turn-in directory : ex04/
Files to turn in : Makefile, main.cpp, *.cpp, *.{h, hpp}
Forbidden functions : std::string::replace

./replace [filename][str1] [str2]

ex04 에서는 filename으로 파일을 열어서 파일에 작성된 str1이라는 문자열을 찾아서 str2로 바꾸고 , filename뒤에 .replace를 붙여서 새로운 파일을 만들도록!

std::ifilestream, std::ofstream

std::ifstream 객체를 생성한다는 것은 파일의 데이터를 읽을 수 있도록 하는 객체를 하나 만들겠다는 의미이다.

std::ofstream 객체를 생성한다는 것은 파일에 데이터를 쓸수 있도록하는 객체를 하나 만들겠다는 의미이다.

std::getline()

std::string::find(), std::string::erase(), std::string::insert()

c++에서 파일 입출력

C++ 은 스트림 관련 기능과 파일 입출력 함수와 같은 관련 함수들을 묶어서 클래스로 제공하고 있다. 즉, C 와 같이 FILE 구조체 를 복잡한 포인터로 사용하지 않고 정보를 객체에 저장한다.
또 기존 C++표준 입출력 (std::cout, std::cin)형식과 유사하게 시프트 연산 (<<, >>) 을 사용할수 있다.

헤더파일 :<fstream>
fstream 헤더 에는 총 3가지 클래스 존재.

  • std::ifstream : read only
  • std::ofstream : write only
  • std::fstream : read & write

파일 스트림에 연결할때는 std::ifstream 과 std::ofstream의 생성자 인자로 파일 이름을 전달하는 방법과 open() 함수를 사용하는 방법을 사용할수 있다.
스트림 작업이 끝나면 close()함수를 호출하면 된다.

std::ifstream과 std::ofstream으로 파일을 열 때는 ios_base::openmode 타입의 flag값을 설정할 수 있는데, std::ifstream은 std::ifstream::in, std::ofstream은 std::ofstream::out이 기본 값으로 설정되어 있다. 기본 값이 아닌 다른 값을 원한다면 별도의 flag들을 명시해야 한다.

ifstream_file.is_open(), ofstream_file.is_open()

  • file stream에 파일이 연결되면 ture, 그렇지 않으면 false

getline()

getline(std::ifstream in, std::string buf)

  • 한 줄씩 읽으며, 개행은 buf에 저장하지 않는다.
    EOF를 만나면 첫번째 인자로 들어온 in의 eofbit를 활성화한다.

C++에는 문자열 관련 라이브러리가 두 가지 존재한다. 첫번째는 '\0' 로 끝나 char * 형식을 따르는 C방식의 문자열 라이브러리 cstring 이고

두번째는 std::string 을 따르는 string 라이브러리다.

따라서 getline() 함수도 두가지 라이브러리에 각기 다른 함수로 존재한다.

istream라이브러리에 속한 cin.getline() 함수와 string 라이브러리에 속한 getline()함수가 있다.

1) istream의 cin.getline()

  • 문자 배열이며 마지막 글자가 '\0' 인 c-string을 입력 받는데 사용
  • n-1개의 문자 개수만틈 읽어와 str에 저장 (n번째 문자는 NULL('\0')로 바꿈)
  • 세번째 인자인 delim은 별도로 지정해주지 않으면 Enter('\n')로 인식
  • delim을 지정해주면 그 제한자(delim)문자 직전 까지 읽어서 str에 저장
istream& getline(char* str, streamsize n);
istream& getline(char* str, streamsize n, char delim);

2) string 의 getline()

  • 최대 문자 수를 입력 하지 않아도 된다.
  • 원하는 구분자(delimiter)를 만날 때 까지 모든 문자열을 입력 받아 하나의 string 객체에 저장
  • 입력 버퍼에 ('\n')가 남아있어 정상 동작 하지 않을 경우 cin.ignore()함수를 사용해 입력 버퍼를 비워주면 된다. ( 무시하는게 아니라..?)
istream& getline(istream& is, string str);
istream& getline(istream& is, string str, char delim);

std::string 클래스의 멤버함수

1) str.find(str2)

  • str문자열에서 str2특정 문자열의 위치 탐색, 찾지 못하면 std::ios::npos 반환.

2) str.erase()

  • 문자열에서 원하는 범위의 문자들을 삭제

3) str.insert()

  • 문자열 중간에 문자열을 추가

4)std::ios::npos

  • std::ios::npos는 -1인 상수이다.

  • std::ios::npos는 자료형이 std::string::size_type 으로 unsigned int 값으로 음수값이 존재 하지 않고 size_type의 가장 큰 수를 의미하게 된다.

5)str.clear()

  • 문자열에 모든 문자들을 제거한다.

6)str1.append(str2)

  • str1 뒤에 str2를 이어 붙인다.

7) str.substr(pos)

  • str의 pos번째를 포함한 하위 문자열을 반환한다.

ex05

Harl 2.0 Turn-in directory : ex05/ Files to turn in : Makefile, main.cpp, Harl.{h, hpp}, Harl.cpp Forbidden functions : None
ex05 과제의 목표는 멤버 함수에 대한 포인터를 사용 해보는 것!!!!

private member function

  • 캡슐화, 은닉성과 관련이 있음
  • 함수를 사용하지만 외부에서 사용할 일이 없는 경우 private 으로 선언해 놓고 사용가능.

function point

  • [반환명][변수명][함수인자] 꼴로 사용
void (Harl::*fptr)(void)

function point 사용 오류

/* levels 멤버함수 포인터 배열 */
void (Harl::*fptr[])(void) = {
	&Harl::debug,
    &Harl::info,
    &Harl::warging,
    &Harl::error
};
/* error case */
for (int i = 0; i < 4; i++)
{
	if (level == levels[i])
    	*fptr[i]();
}
/* fixed case */
for (int i =0; i< 4; i++)
{
	if (level == levels[i])
    	(this->*fptr[i])();
}

if, else 문 없이 함수포인터 사용하기 -배열 사용하기

std::string levels[] = {
	"debug",
    "info",
    "warging",
    "error"
    };
void (Harl::*fptr[])(void) = {
	&Harl::debub,
    &Harl::info,
    &Harl::warning,
    &Harl::error
   };
  • map함수를 사용하면 더 쉽고 직관적이겠지만 , cpp08 전 까지는 STL사용 금지 되었기때문에 각 배열을 따로 선언해주고 인덱스가 같게끔 해서 연결해주는 방법을 사용

함수 포인터

프로그램에서 정의된 함수는 프로그램이 실행될때 모두 메인 메모리에 올라간다.
이때 함수의 이름은 메모리에 올라간 함수의 시작 주소를 가리키는 포인터상수(constant pointer) 가 된다. 이렇게 함수의 시작주소를 가리키는 포인터 상수를 함수 포인터(function pointer)라고 부른다.

즉, 함수포인터는 함수가 저장된 메모리 주소를 저장할 수있는 변수로 함수 호출할 수있는 또 다른 방법을 제시해준다.

포인터 상수란(constant pointer) 란 포인터 변수가 가리키고 있는 주소값을 변경할 수 없는 포인터를 의미하며,
상수 포인터(pointer to constant) 란 상수를 가리키는 포인터를 의미한다.

/* 함수 포인터 선언 */
[반환 타입] (*[함수 포인터명])([매개변수]);

/* 함수 포인터에 함수 할당 */
[함수 포인터명] = [함수명];
[함수 포인터명] = &[함수명];

/* 선언과 동시에 할당 */
[반환 타입] (*[함수 포인터명])([매개변수]) = [함수명];
[반환 타입] (*[함수 포인터명])([매개변수]) = &[함수명];

/* 함수 포인터로 함수 호출 */
[함수 포인터명]([매개변수]);
(*[함수 포인터명])([매개변수]);
  • 전역함수에 대한 함수 포인터 사용방법
#include <iostream>
int plus(int x, int y)
{
	return (x+y);
}

int minus(int x, int y)
{
	return (x-y);
}

int main()
{
	int (*fn)(int,int);						// 함수포인터 변수를 선언

	fn = plus;								// 함수포인터 초기화 방법1
	std::cout << fn(5,2) << std::endl;		// 함수포인터 호출 방법1

	fn = &minus;							// 함수포인터 초기화 방법2
	std::cout << (*fn)(5,2) << std::endl;	// 함수포인터 호출 방법2
	return 0;
}
  • 방법1의 경우 암시적 으로 작성
  • 방법2 의 경우 명시적으로 작성
  • fn은 함수의 주소값을 담는 변수이므로 함수의 주소값을 넣어줘야 한다.
  • 따라서 방법2는 &를 사용해서 명시적으로 함수의 주소값을 넣어주었습니다
  • 방법1의 경우 &을 사용하지 않았지만 컴파일시 &가 들어가서 함수 포인터 변수를 초기화 한다고 생각할수 있다.
  • 호출할때 역시 fn은 함수의 주소값이므로 그 주소가 가리키는 함수를 호출해야합니다.
  • 따라서 방법2는 *를 사용해서 명시적으로 그 주소가 가리키는 함수에 접근하여 함수를 호출했습니다.
  • 방법1의 경우 를 사용하지 않았지만 컴파일시 가 붙어서 함수를 호출한다 생각할수 있겠슴다.

전역함수에 대한 포인터 배열 사용법
함수 포인터 배열은 함수포인터에 []를 작성해서 사용할수 있다.
함수 포인터 배열 역시 암시적, 명시적 두 방식으로 함수를 호출, 초기화 할수있다.

#include <iostream>

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

int minus(int x, int y) {
	return (x - y);
}

int main() {
	int (*fnarr[2])(int,int);                     // 함수포인터 배열 선언

	fnarr[0] = plus;                              // 함수포인터 배열 초기화 방법1
	fnarr[1] = &minus;                            // 함수포인터 배열 초기화 방법2

	std::cout << (fnarr[0])(9, 2) << std::endl;   // 함수포인터 배열 호출 방법1
	std::cout << (*fnarr[1])(9, 2) << std::endl;  // 함수포인터 배열 호출 방법2
	return 0;
}

멤버 함수 포인터

static 을 제외한 클래스 멤버 함수는 앞에 this가 생략 되어 있다.
this 는 자기자신의 메모리 주소를 가리키는 포인터로 생성된 인스턴스의 주소가 담겨있다.
그러므로 멤버함수를 일반 함수 포인터에 할당하면 형식이 맞지 않는다는 오류를 확인할 수 있다.

멤버 함수 포인터 는 원하는 함수 포인터 이름으로 바로 선언할 수 있었던 일반 함수 포인터와 달리 어떤 클래스에 속해 있는지 :: 연산자와 함께 명시하여 선언해야한다.
할당할 때도 마찬가지로 함수의 클래스를 명시하고 클래스명 앞에 &연산자 또한 함께 써야한다.

/* 함수 포인터 선언 */
[반환 타입] ([클래스명]::*[멤버 함수 포인터명])([매개변수]);

/* 함수 포인터에 함수 할당 */
[멤버 함수 포인터명] = &[클래스명]::[멤버 함수명];

/* 선언과 동시에 할당 */
[반환 타입] ([클래스명]::*[멤버 함수 포인터명])([매개변수]) = &[클래스명]::[멤버 함수명];

/* 함수 포인터로 함수 호출 */
(this->*[함수 포인터명])([매개변수]);

/* 클래스 외부에서 함수 호출 */
[클래스명] [인스턴스명] = [생성자];				// 인스턴스 생성
([인스턴스명].*[멤버 함수 포인터명])([매개변수]);	// 인스턴스를 활용하여 함수 호출

클래스에 선언된 멤버함수에 대한 포인터 사용법
전역함수의 경우 암시적으로 함수포인터를 초기화하고 호출 할수있었지만,

멤버함수에 대한 함수포인터를 사용할때에는 꼭 명시적으로 함수포인터를 초기화 하고 호출해야 합니다.

그리고 추가된 점은 함수에 명시적으로 접근하기 위해서 함수를 클래스::함수 와 같은 형태로 접근해야 합니다.

#include <iostream>

class Cal
{
	private:
		int plus(int x, int y)
		{
			return (x+y);
		}
		int minus(int x, int y)
		{
			return (x-y);
		}
	public:
		void calcul(const std::string &str, int x, int y)
		{
			int (Cal::*fn)(int,int);

			if (str == "plus")
			{
				fn = &Cal::plus;
			}
			else if (str == "minus")
			{
				fn = &Cal::minus;
			}
			std::cout << (this->*fn)(x,y) << std::endl;
		}
};

int main(void)
{
	Cal cal;
	cal.calcul("plus",5,2);
	cal.calcul("minus",5,2);
	return (0);
}

클래스에 선언된 멤버함수에 대한 함수 포인터배열 사용법

#include <iostream>

class Calculate
{
private:
	int plus(int x, int y) {
		return (x + y);
	}

	int minus(int x, int y) {
		return (x - y);
	}

public:
	void calculate(int x, int y) {
		int (Calculate::*fn[2])(int, int);

		fn[0] = &Calculate::plus;
		fn[1] = &Calculate::minus;

		for (int i = 0; i < 2; i++)
			std::cout << (this->*fn[i])(x, y) << std::endl;
	}
};

int main(void) {
	Calculate cal;

	cal.calculate(5, 2);
	return (0);
}

static const 초기화

static const 멤버 변수를 클래스 안에서 초기화할 수있는 경우는
1. integral 타입(int , float 등..)
2. enum 타입
의 경우에만 가능.
그 외에는 class 밖에서 초기화해야 합니다.

class A
{
private:
	static const int INT_NUM = 10;
    static const float FLOAT_NUM = 10.2;
    static const double COUBLE_NUM = 10.22;
    static const std::string COMMENT;
    static const std::string COMMENT[];
};

const std::string A:COMMENT = "karen";
const std::string A:COMMENT[4] = {
	"DEBUG",
    "INFO",
    "WARNING",
    "ERROR"
};

ex06 (Harl filter)

Harl filter Turn-in directory : ex06/ Files to turn in : Makefile, main.cpp, Harl.{h, hpp}, Harl.cpp Forbidden functions : None
ex05에서 filtering을 위한 요소만 추가하면 된다. main()의 인자로 요구가 들어오는데 해당 요구가 일치할 경우 그 요구 이상의 모든 로그를 출력해야 한다. 이를 구현하기 위한 가장 적절한 방법은 switch문을 사용하는 것이다.

profile
be pro

0개의 댓글