- C++ 표준 문자열 (std::string)
- 상속 (inheritance)
- 오버라이딩(overriding)
- protected 키워드
짧은 문자열에 대해서는 동적으로 메모리를 할당하지 않고 그냥 지역변수로 보관을 하고,
문자열을 복사 할때 그 복사된 문자열의 내용이 바뀌지 않는한 실제로 데이터를 복사하는것이 아니라 원래 문자열을 가리키기만 한다,
(어떤 라이브러리에 사용함에 따라 아닐수도 있음).
#include <iostream>
#include <string>
int main()
{
std::string s = "abc";
std::cout << s << std::endl;
return (0);
}
"abc"는 컴파일러 상에서 C형식 문자열로 인식됩니다.
즉, 위 문장은 string
클래스의 인자를 const char *
로 받는 생성자를 호출한것.
#include <iostream>
#include <string>
int main()
{
std::string s = "abc";
std::string t = "def";
std::string s2 = s;
std::cout << s << " 의 길길이 : " << s.length() << std::endl;
std::cout << s << " 뒤에 " << t << " 를 붙이면 " << s + t << std::endl;
if (s == s2)
{
std::cout << s << " 와 " << s2 << " 는 같다 " << std::endl;
}
if (s != t)
{
std::cout << s <<" 와 " << t << "는 다르다" << std::endl;
}
return (0);
}
if (s == s2) std::cout << s << " 와 " << s2 << " 는 같다 " << std::endl;
string 제공하는 함수들 참고 (+ insert , erase, replace...)
https://modoocode.com/233
회사의 사원들의 월급을 계산해서 한달에 총 얼마나 되는 돈을 월급으로 지출해야 하는지 알려주는 단순한 프로그램.
먼저 각 사원들의 정보를 클래스로 만들어서 데이터를 보관하도록해보자.
사원들의 필요한 데이터는 이름, 나이, 직책, 직책 넘버
#include <iostream>
#include <string>
class Employee
{
private:
std::string _name;
int _age;
std::string _pos; // 직책(이름)
int _rank; // 직책 순위(클수록 높음)
public:
Employee(std::string name, int age, std::string pos, int rank)
: _name(name), _age(age), _pos(pos), _rank(rank) {}
// 복사 생성자
Employee(const Employee& employee)
{
_name = employee._name;
_age = employee._age;
_pos = employee._pos;
_rank = employee._rank;
}
// 디폴트 생성자
Employee() {}
void print_info()
{
std::cout << _name << " (" << _pos << " , " << _age << ") ==> "
<< calcul_pay() << "만원" <<std::endl;
}
int calcul_pay()
{
return (200+_rank*50);
}
};
int main()
{
Employee s1("junyopar", 123, "high", 123);
Employee s2("elonmusk", 122, "mid", 122);
s1.print_info();
s2.print_info();
}
아래 멤버 변수들을 이용해 사원 데이터 처리.
int alloc_emp; //할당한 총 직원수 - 할당된 크기를 알려주는 배열
int curr_emp; // 현재 직원수 - 현재 emp_list에 등록된 사원수
Employee **emp_list;// 직원 데이터
emp_list
가 Employ**
타입으로 되어있는 이유EmployeeList(int alloc_emp): alloc_emp(alloc_emp)
{
emp_list = new Employee*[alloc_emp];
curr_emp = 0;
}
void add_emp(Emplyee* emp)
{
// 사실 current_employee 보다 alloc_employee 가 더
// 많아지는 경우 반드시 재할당을 해야 하지만, 여기서는
// 최대한 단순하게 생각해서 alloc_employee 는
// 언제나 current_employee 보다 크다고 생각한다.
// (즉 할당된 크기는 현재 총 직원수 보다 많음)
emp_list[curr_emp] = emp;
curr_emp++;
}
code
#include <iostream>
#include <string>
class Employee
{
private:
std::string _name;
int _age;
std::string _pos; // 직책(이름)
int _rank; // 직책 순위(클수록 높음)
public:
Employee(std::string name, int age, std::string pos, int rank)
: _name(name), _age(age), _pos(pos), _rank(rank) {}
// 복사 생성자
Employee(const Employee& employee)
{
_name = employee._name;
_age = employee._age;
_pos = employee._pos;
_rank = employee._rank;
}
// 디폴트 생성자
Employee() {}
void print_info()
{
std::cout << _name << " (" << _pos << " , " << _age << ") ==> "
<< calcul_pay() << "만원" <<std::endl;
}
int calcul_pay()
{
return (200+_rank*50);
}
};
class EmployeeList
{
private:
int alloc_emp;
int curr_emp;
Employee** emp_list;
public:
EmployeeList(int alloc_emp) : alloc_emp(alloc_emp)
{
emp_list = new Employee*[alloc_emp];
curr_emp = 0;
}
void add_emp(Employee* emp)
{
emp_list[curr_emp] = emp;
curr_emp++;
}
int curr_emp_num() { return curr_emp; }
void print_emp_info()
{
int total_pay = 0;
for (int i= 0; i < curr_emp; i++)
{
emp_list[i]->print_info();
total_pay += emp_list[i]->calcul_pay();
}
std::cout << "total pay : " << total_pay << "만원 " << std::endl;
}
~EmployeeList()
{
for (int i=0; i < curr_emp; i++)
{
delete emp_list[i];
}
delete[] emp_list;
}
};
int main()
{
Employee s1("junyopar", 123, "high", 123);
Employee s2("elonmusk", 122, "mid", 122);
s1.print_info();
s2.print_info();
EmployeeList emp_list(10);
emp_list.add_emp(new Employee("노홍철", 34, "평사원", 1));
emp_list.add_emp(new Employee("하하", 34, "평사원", 1));
emp_list.add_emp(new Employee("유재석", 41, "부장", 7));
emp_list.add_emp(new Employee("정준하", 43, "과장", 4));
emp_list.add_emp(new Employee("박명수", 43, "차장", 5));
emp_list.add_emp(new Employee("정형돈", 36, "대리", 2));
emp_list.add_emp(new Employee("길", 36, "인턴", -2));
emp_list.print_emp_info();
while (1)
;
}
근속 년수를 포함시켜라 특정 직급에는
Manager 클래스 코드
class Manager {
std::string name;
int age;
std::string position; // 직책 (이름)
int rank; // 순위 (값이 클 수록 높은 순위)
int year_of_service;
public:
Manager(std::string name, int age, std::string position, int rank,
int year_of_service)
: year_of_service(year_of_service),
name(name),
age(age),
position(position),
rank(rank) {}
// 복사 생성자
Manager(const Manager& manager) {
name = manager.name;
age = manager.age;
position = manager.position;
rank = manager.rank;
year_of_service = manager.year_of_service;
}
// 디폴트 생성자
Manager() {}
int calculate_pay() { return 200 + rank * 50 + 5 * year_of_service; }
void print_info() {
std::cout << name << " (" << position << " , " << age << ", "
<< year_of_service << "년차) ==> " << calculate_pay() << "만원"
<< std::endl;
}
}
~~ 그래서 상속!!
위의 Manager 클래스의 코드 자체가 Employee의 대부분을 포함하고 있기때문에.
C++에서는 이와 같은 경우, 다른 클래스의 내용을 그대로 포함할수있는 작업을 가능하도록 해준다
그게 바로 상속이다.
C++ 에서 상속을 통해 다른 클래스의 정보를 물려받아서 사용할수 있다.
간단한 예시를 보자.
#include <iostream>
#include <string>
class Base
{
std::string s;
public:
Base(): s("기반")
{
std::cout << "기반 클래스" << std::endl;
}
void what()
{
std::cout << s << std::endl;
}
};
기반 클래스, 아래는 기반클래스(Base)를 물려받을 파생 (Derived)클래스의 모습
- 보톡 부모-자식 클래스라고도 한다.
- 근데 여러명의 부모를 가진다는 어감 때문에 기반, 파생 클래스 라 부르는것을 선호.
class Derived : public Base
{
std::string s;
public:
Derived() : Base(), s("파생")
{
std::cout << "파생 클래스" << std::endl;
// Base에서 what()물려 받았으므로
// Derived 에서 호출 가능하다.
what();
}
};
class Derived : public Base
Derived() : Base(), s("파생") {
std::cout << "파생 클래스" << std::endl;
// Base 에서 what() 을 물려 받았으므로
// Derived 에서 당연히 호출 가능하다
what();
}
Derived 의 생성자 호출 부분
위처럼 초기화 리스트기반의 생성자를 호출해서 기반의 생성을 먼저 처리한 다음에 ,
Derived의 생성자가 실행 되어야 한다.
따라서 아래처럼
Derived () : Base() ,s("파생")
초기화 리스트에서 Base를 통해 기반의 생성자를 먼조 호출하게 된다.
참고로 기반클래스의 생성자를 명시적으로 호출하지 않을 경우
디폴트 생성자 호출 된다.
위에서 Derived 의 s에 "파생" 을 넣게 되고,
Dervied 생성자 내부를 실행하기 전에
Base의 생성자를 먼저 호출한다.
따라서 파생 클래스 생성 바로 아래에 파생 클래스가 출력하기 이전에 Base 의 생성자가 호출되어서 기반 클래스가 먼저 출력하게 되는것
what()
what() 호출하는 부분을 보자
#include <iostream>
#include <string>
class Base
{
std::string s;
public:
Base(): s("기반")
{
std::cout << "기반 클래스" << std::endl;
}
//void what()
//{
// std::cout << s << std::endl;
//}
};
class Derived : public Base
{
std::string s;
public:
Derived() : Base(), s("파생")
{
std::cout << "파생 클래스" << std::endl;
// Base에서 what()물려 받았으므로
// Derived 에서 호출 가능하다.
what();
}
void what()
{
std::cout << s <<std::endl;
}
};
int main()
{
std::cout << " === 기반 클래스 생성 === " << std::endl;
Base p;
std::cout << " === 파생 클래스 생성 === " << std::endl;
Derived c;
return 0;
}
>>
=== 기반 클래스 생성 ===
기반 클래스
=== 파생 클래스 생성 ===
기반 클래스
파생 클래스
기반
#include <iostream>
#include <string>
class Base
{
std::string s;
public:
Base(): s("기반")
{
std::cout << "기반 클래스" << std::endl;
}
void what()
{
std::cout << s << std::endl;
}
};
class Derived : public Base
{
std::string s;
public:
Derived() : Base(), s("파생")
{
std::cout << "파생 클래스" << std::endl;
// Base에서 what()물려 받았으므로
// Derived 에서 호출 가능하다.
what();
}
void what()
{
std::cout << s <<std::endl;
}
};
int main()
{
std::cout << " === 기반 클래스 생성 === " << std::endl;
Base p;
std::cout << " === 파생 클래스 생성 === " << std::endl;
Derived c;
return 0;
}
>>
=== 기반 클래스 생성 ===
기반 클래스
=== 파생 클래스 생성 ===
기반 클래스
파생 클래스
파생
Dervied와 Base 에 둘다 what() 함수가 정의 되어 있다.
이경우 Dervied 에서 what을 호출하게되면 무엇을 호출할까?
사실 두 함수는 같은 이름이지만(인자도 같지만)
다른 클래스에 정의되어 있기 때문에 다른 함수로 취급된다.
위의 경우 Dervied 에 what함수가 정의 되어 있기 때문에 Dervied 생성자에서 what을 호출할때 굳이 멀리 Base으 함수 까지 뒤지지 않고
바로 앞에 있는 Dervied 의 what을 호출하게 된다.
이런것을 가리켜 오버라이딩 이라고 한다.
즉 Derived 의 what 함수가 Base의 what함수를 오버라이딩 한것.
- 간혹 함수 오버로딩 과 혼동하는데 ,
- 오버로딩은 같은 이름의 함수를 인자를 달리 하여 정의하는것을 의미.
- 상속에서 오버라이딩 과는 전혀 다른 이야기.
#include <iostream>
#include <string>
class Base
{
private:
std::string parent_string;
public:
Base() : parent_string("기반") { std::cout << "기반 클래스" << std::endl; }
void what() { std::cout << parent_string << std::endl; }
};
class Derived : public Base {
std::string child_string;
public:
Derived() : child_string("파생"), Base()
{
std::cout << "파생 클래스" << std::endl;
// 그렇다면 현재 private 인 Base 의
// parent_string 에 접근할 수 있을까?
parent_string = "바꾸기";
}
void what() { std::cout << child_string << std::endl; }
};
이코드는 컴파일 에러.
private 멤버 변수들은 그 어떠한 경우에서도 자기 클래스 말고는 접근 할 수없다.
하지만 상속 받은 클래스에서 직접 기반 클래스의 private 데이터에 접근할 필요가 있다.
C++에서 다행히도 private , protected ,public 즉 중간 단계인 protected 키워드를 둬서
상속 받는 클래스에서는 접근 가능하고 그외의 기타 정보는 접근 불가능
#include <iostream>
#include <string>
class Base
{
protected:
std::string parent_string;
public:
Base() : parent_string("기반") { std::cout << "기반 클래스" << std::endl; }
void what() { std::cout << parent_string << std::endl; }
};
class Derived : public Base {
std::string child_string;
public:
Derived() : child_string("파생"), Base()
{
std::cout << "파생 클래스" << std::endl;
// 그렇다면 현재 private 인 Base 의
// parent_string 에 접근할 수 있을까?
parent_string = "바꾸기";
}
void what() { std::cout << child_string << std::endl; }
};
int main() {}
얘는 컴파일 됨!
class Dervied : public Base
위 코드 에서 public 키워드의 의미.
만일 위처럼 public 형태로 상속하였다면
만일 proted 로 상속 했다면
만일 private로 상속 했다면
#include <iostream>
#include <string>
class Base
{
public:
std::string s;
Base(): s("기반")
{
std::cout << "기반 클래스" << std::endl;
}
void what()
{
std::cout << s << std::endl;
}
};
class Derived : private Base
{
std::string s;
public:
Derived() : Base(), s("파생")
{
std::cout << "파생 클래스" << std::endl;
// Base에서 what()물려 받았으므로
// Derived 에서 호출 가능하다.
what();
}
void what()
{
std::cout << s <<std::endl;
}
};
int main()
{
std::cout << " === 기반 클래스 생성 === " << std::endl;
Base p;
std::cout << " === 파생 클래스 생성 === " << std::endl;
Derived c;
return 0;
}
//
사원 프로그램에 적용시켜보기 ~
pass