2022. 3. 17. 18:19ㆍ강의 내용 정리/자료구조
소프트웨어 공학 원리
1. S/W revolution
1) 프로그래밍 초기 단계
- 프로그래밍은 단순한 문제 해결만 가능하고 적은 수의 프로그래머밖에 없었다. 또한 컴퓨터의 큰 비용 문제가 있었다.
- 프로그램의 목적은 문제 해결을 위한 방법 및 행위에만 초점을 맞췄다.
- 군사용으로 등장했다. 또한 컴퓨터가 과학자들보다 몸값이 높았다. 이에 따라 컴파일 과정을 수작업으로 진행했다.
2) 프로그래밍 과도기
- 프로그래밍은 보다 큰 문제 해결을 위한 것에 목적이 있었지만 역시나 문제 해결을 위한 방법에 초점을 맞췄다.
- 타이트한 데드라인에 맞춰 개발
- 대표적인 언어가 c언어임
3) 프로그래밍 현재
- 프로그래밍은 아주 큰 문제 해결에 초점을 맞추고 문제 해결 방법보단 데이터가 더 중요하다.
- 항상 가능한 빨리 제작하고 S/W의 재사용성이 중요해졌다.
4) 프로그래밍의 미래
- 컴퓨터가 알아서 코딩한다.
5) 프로그래머의 업무
프로그래머 관점에서의 협의의 업무
- 상호 간에 협의된 인터페이스를 사용해서 코드를 작성한다.
2. 객체 지향 프로그래밍
1) S/W 객체란?
- 클래스: 어떤 구현하고자 하는 것에 대한 동작이나 함수, 변수 등을 만드는 것
- 객체: 클래스를 통해 만든 것
- 상속: 일반화된 클래스를 기반으로 관련된 하나의 객체를 도출하는 능력
- 데이터는 private으로 만들어서 메소드를 통해 이에 접근하고자 한다.
- 멤버 함수는 called and standard functions에 들어가있는다. -> 객체에서 호출한다면 this 포인터를 사용해 호출할 수 있다.
2) 객체지향 프로그래밍
- 느리지만 프로그래머의 몸값이 비싸지기 때문에 개발 시간 단축을 위해 사용
- 개발에 들어가는 비용 중 하드웨어보단 프로그래머의 비용이 더 늘어서 개발 시간 단축이 더 큰 비용 감소를 이끌어낸다.
3) Private / Public
- Private: 클래스 외부에서 멤버 변수의 접근을 막기 위한 지시자
- Public: 클래스 외부에서 멤버 변수의 접근이 가능한 지시자
- 처리를 위해 반드시 필요한 데이터이지만 사용자가 알 필요가 없는 멤버 변수는 private으로 제한을 둬서 데이터 훼손을 방지하기 위해서 제한 지시자를 사용함. ex) 딥러닝에서의 데이터
- Private을 사용해서 멤버 변수에 대한 접근을 제한하면 이에 사용자가 접근하려 했을 때 syntax error 발생
- 멤버 데이터를 악용하는 것을 방지하는 역할을 가지기도 함
- 함수 앞에 m_를 넣는 경우에는 일반적으로 private 멤버로 사용한다는 의미임 (Private은 _를 앞에 넣는다.)
- Private을 외부에서 사용하기 위해선 friend를 선언해야함
cf) const: 상수를 사용하기 위해 선언할 때 사용 / 함수에서 파라미터에 const를 넣기도 함
-> operater 재정의할 때 사용하기도 함
- static: 컴파일 시간에 잡혀있고 전역 상에서 잡혀있는 데이터로 클래스에 하나만 존재한다. 이는 객체가 존재하지 않아도 동작시킬 수 있고, counting 같은 것을 사용할 때 활용함
- 사람들이 프로그램에 대해 아예 모른다고 가정하고 짜야한다.
- 재귀호출이나 디자인 패턴은 Private 함수로 들어갈 수 있다. ex) 싱글톤 패턴 // 객체를 하나만 생성해야한다. 이에 따라 복사 생성자나 생성자를 막아놔야한다.
4) 클래스 네가지 특성
(1) 데이터 은닉성(Data Hiding)
(2) 데이터 캡슐화(Data Encapsulation)
(3) 상속(Inheritance)
(4) 다형성(Polymorpism)
5) 구조체와 클래스
(1) struct
- 관용적으로 멤버 변수의 집합으로서 구현하는 경우가 많음
- default 접근 지시자가 public임-> class의 default 접근 지시자는 private임
(2) class
- 사용자 정의형 데이터 타입의 일종
- 클래스 외부에서 함수를 정의할 경우에는 클래스 선언 이후에 할 수 있다. 이때 Private도 사용할 수 있다.
cf) struct/union/enum 또한 사용자 정의형 데이터 타입이긴 하다.
cf) union: 멤버 변수를 각각 선언하지만 해석하는 형태를 다양하게 할 수 있다.
6) Class
(1) this 포인터
- 선언된 객체의 주소를 가지는 상수 포인터
- 객체는 여러 개가 있을 수 있지만 멤버 함수는 하나로 정의되어있기 때문에 각각의 객체 마다 함수를 만드는 것이 아니라 멤버 함수는 한 copy만 만들어서 관리함 -> 이에 따라 어떤 객체가 함수를 호출했는지 구분해줄 필요가 있기에 해당 객체의 포인터를 넘겨준다.
(2) 생성자
- 아무것도 적지 않았을 때 디폴트 생성자가 불러짐
- 생성자를 불렀을 때는 디폴트 생성자가 불러지지 않기 때문에 이를 사용하기 위해선 디폴트 생성자를 추가로 만들어야함
- 생성자는 여러개를 정의할 수 있고, 리턴타입이 없다.
i) 복사 생성자: 이미 존재하는 동일한 타입의 객체를 복사해서 새로운 객체를 만들 때 호출됨(= 기호 사용) -> 선언과 동시에 대입을 하는 경우 복사 생성자가 호출됨
- 소괄호를 열 때도 복사 생성자가 호출된다.
ii) 대입 생성자: 이미 존재하는 동일한 타입의 객체를 대입해서 새로운 객체를 만들 때 호출됨(= 기호 사용) -> 선언과 대입이 구분될 때 대입 생성자가 호출됨
(3) 소멸자
- 생성자와는 다르게 하나만 존재할 수 있다. 클래스의 이름을 사용해 생성자는 파라미터로 구분할 수 있기에 명시적으로 호출하지 않더라도 여러개를 사용할 수 있는 반면 소멸자는 명시적으로 호출할 수 없기에 구분할 수 없고 이에 따라 하나밖에 만들 수 없다.
- 부모의 포인터로 자식을 만들었을 때 메모리 해제가 애매해지기에 virtual 키워드를 부모의 소멸자에 집어넣어야한다. 이에 따라 자식부분까지 메모리 해제를 할 수 있다. 특히 동적할당을 하는 경우 자식 클래스의 소멸자가 호출되지 않는 경우에 virtual 키워드를 통해 메모리 누수를 방지할 수 있다.
#include <iostream>
class Base {
public:
Base() { std::cout << "Base constructor" << std::endl; };
virtual ~Base() { std::cout << "Base destructor" << std::endl; };
private:
};
class Derived : public Base {
public:
Derived() { std::cout << "Derived constructor" << std::endl; };
~Derived() { std::cout << "Derived destructor" << std::endl; };
private:
};
Derived* getDerived() {
return new Derived();
}
int main() {
Base *t = Derived();
delete(t);
}
7) Copy
(1) bitwise copy: copy하고자 하는 객체와 동일한 메모리를 가리킴
(2) logical copy: 다른 메모리에 동일한 객체를 만듦
3. 소프트웨어 공학
1) 좋은 소프트웨어 목적
- 동작되고 쉽고, 효과적이게 수정가능해야한다.
- 가독성이 뛰어나야한다. 이를 위해 주석을 잘 짜야한다.
- 재사용이 가능해야한다.
2) Program Specification
- 프로그램이 무엇을 하는지 작성해야한다.
3) 검증
(1) Verification: 올바른 과정에 집중
(2) Validaton: 올바른 결과에 집중
4) 전제조건과 사후조건
(1) 전제조건: 실행을 시작하기 전에 함수가 참이어야하는 명제를 설정하는 가정
(2) 사후조건: 함수가 실행을 실행을 완료하는 순간에 무엇이 참이어야 하는지를 설명
(3) 호출자: 전제조건을 보장할 책임이 있으며 함수 코드는 사후조건을 보장해야함
5) 개발에서의 검사
검사의 목적은 프로그래머의 디자인 또는 구현에 대한 토론을 유도하는 것
(1) Walk-Through (대략적 검사)
- 칠판 및 종이에 손으로 쓰면서 자료를 추적함으로써 프로그램의 요구사항, 설계, 구현방법 등을 대략적으로 검사하는 방법 -> 디자인을 잡는 과정에서 여러가지를 다듬기에 더 많이 한다.
(2) Inspection (정밀 검사)
- 한 멤버가 프로그램이나 디자인을 한 줄씩 읽고 다른 멤버가 오류를 지적하여 확인하는 방법
(3) Integration testing
- 독립적인 모듈을 단위별로 테스트를 거친 뒤 나중에 모듈을 통합하는 과정이 중요함
- 통합적인 테스트를 하기위해 실행단위에 대한 독립적인 모듈을 먼저 테스트하는 것을 전제조건으로 둘 수 있다.
(4) 검사 방식
- Top-down 방식: 위에서부터 쭉 내려와서 검사가 진행되는 방식
- Bottom-up 방식: 아래에서부터 위로 올라가며 검사가 진행되는 방식
'강의 내용 정리 > 자료구조' 카테고리의 다른 글
자료구조 (6), Linked Structured (0) | 2022.05.02 |
---|---|
자료구조 (5), Stack and Queue (0) | 2022.04.19 |
자료구조 (4) ADTs Unsorted List and Sorted List (0) | 2022.03.17 |
자료구조 (3) 데이터 디자인과 구현 (0) | 2022.03.17 |
자료구조 (1) 객체 지향 프로그래밍 (0) | 2022.03.08 |