9.1주차
실습 1
문제 설명
배열 arr가 주어집니다. 배열 arr의 각 원소는 숫자 0부터 9까지로 이루어져 있습니다. 이때, 배열 arr에서 연속적으로 나타나는 숫자는 하나만 남기고 전부 제거하려고 합니다. 단, 제거된 후 남은 수들을 반환할 때는 배열 arr의 원소들의 순서를 유지해야 합니다. 예를 들면,
arr = [1, 1, 3, 3, 0, 1, 1] 이면 [1, 3, 0, 1] 을 return 합니다.
arr = [4, 4, 4, 3, 3] 이면 [4, 3] 을 return 합니다.
배열 arr에서 연속적으로 나타나는 숫자는 제거하고 남은 수들을 return 하는 solution 함수를 완성해 주세요.
제한사항
배열 arr의 크기 : 1,000,000 이하의 자연수
배열 arr의 원소의 크기 : 0보다 크거나 같고 9보다 작거나 같은 정수
입출력 예
arr | answer |
---|---|
[1,1,3,3,0,1,1] | [1,3,0,1] |
[4,4,4,3,3] | [4,3] |
내가 제출한 코드
#include <vector>
#include <iostream>
using namespace std;
vector<int> solution(vector<int> arr)
{
vector<int> answer;
for(int i=0; i<arr.size()-1; i++){
if(arr[i] != arr[i+1]){
answer.push_back(arr[i]);
}
}
answer.push_back(arr[arr.size()-1]);
return answer;
}
교수님 코드
vector<int> solution(vector<int> arr)
{
vector<int> answer;
answer.push_back(arr[0]);
for (int i = 1; i < arr.size(); i++)
{
if (arr[i] != arr[i + 1])
answer.push_back(arr[i]);
}
return answer;
}
실습 2
문제 설명
명함 지갑을 만드는 회사에서 지갑의 크기를 정하려고 합니다. 다양한 모양과 크기의 명함들을 모두 수납할 수 있으면서, 작아서 들고 다니기 편한 지갑을 만들어야 합니다. 이러한 요건을 만족하는 지갑을 만들기 위해 디자인팀은 모든 명함의 가로 길이와 세로 길이를 조사했습니다.
아래 표는 4가지 명함의 가로 길이와 세로 길이를 나타냅니다.
명함 번호 | 가로 길이 | 세로 길이 |
---|---|---|
1 | 60 | 50 |
2 | 30 | 70 |
3 | 60 | 30 |
4 | 80 | 40 |
가장 긴 가로 길이와 세로 길이가 각각 80, 70이기 때문에 80(가로) x 70(세로) 크기의 지갑을 만들면 모든 명함들을 수납할 수 있습니다. 하지만 2번 명함을 가로로 눕혀 수납한다면 80(가로) x 50(세로) 크기의 지갑으로 모든 명함들을 수납할 수 있습니다. 이때의 지갑 크기는 4000(=80 x 50)입니다.
모든 명함의 가로 길이와 세로 길이를 나타내는 2차원 배열 sizes가 매개변수로 주어집니다. 모든 명함을 수납할 수 있는 가장 작은 지갑을 만들 때, 지갑의 크기를 return 하도록 solution 함수를 완성해주세요.
제한사항
sizes의 길이는 1 이상 10,000 이하입니다.
sizes의 원소는 [w, h] 형식입니다.
w는 명함의 가로 길이를 나타냅니다.
h는 명함의 세로 길이를 나타냅니다.
w와 h는 1 이상 1,000 이하인 자연수입니다.
입출력 예
sizes | result |
---|---|
[[60, 50], [30, 70], [60, 30], [80, 40]] | 4000 |
[[10, 7], [12, 3], [8, 15], [14, 7], [5, 15]] | 120 |
[[14, 4], [19, 6], [6, 16], [18, 7], [7, 11]] | 133 |
내가 제출한 코드
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
int solution(vector<vector<int>> sizes) {
int answer = 0;
vector<int> max;
vector<int> min;
for (int i = 0; i < sizes.size(); i++)
{
if (sizes[i][0] < sizes[i][1])
{
max.push_back(sizes[i][1]);
min.push_back(sizes[i][0]);
}
else
{
max.push_back(sizes[i][0]);
min.push_back(sizes[i][1]);
}
}
sort(max.begin(), max.end());
sort(min.begin(), min.end());
answer = max[max.size() - 1] * min[min.size() - 1];
return answer;
}
교수님 코드
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
int solution(vector<vector<int>> sizes) {
int answer = 0;
int minmax = 0;
int maxmax = 0;
for (int i = 0; i < sizes.size(); i++)
{
if (sizes[i][0] > sizes[i][1])
{
if (sizes[i][0] > maxmax)
maxmax = sizes[i][0];
if (sizes[i][1] > minmax)
minmax = sizes[i][1];
}
else
{
if (sizes[i][1] > maxmax)
maxmax = sizes[i][1];
if (sizes[i][0] > minmax)
minmax = sizes[i][0];
}
}
return answer;
}
클래스
클래스가 나오게된 계기
변수의 경우는 글로벌 변수, 로컬 변수로 나뉠 수가 있다. 스코프를 이용해서! 근데 함수의 경우는 따지고 보면 무조건 글로벌 함수가 되어버린다. 근데 함수 역시 글로벌은 그렇게 좋지 않다
왜?
- 시작 시점부터 메모리를 차지해서 프로그램 종료 시점까지 살아 있다.
- 프로그램의 모든 위치에서 access가 가능하다. 이렇게 된다면 함수를 통해서 만들어진 값에 안정성이 없다.
함수의 경우는 변경의 대상은 아니지만 변경의 수단이 될 수 있다. 그렇기 때문에 프로그램에서는 “간접적”인 불안요소가 되는 것이다. 따라서 이를 해결하기 위해서 담을 만들고자 하였고, 변수와 함수 주변에 테두리를 만들어서 테두리 안에 있는 친구들만 변수, 함수를 사용하고자 만들었다. 즉, 클래스라는 담을 치는 격이 되는 것이다.
default: private이고 각각의 클래스끼리는 못보게 하자. 그 클래스 안에 있는 애들만 볼 수 있도록 하는게 일차적인 원칙이고 이걸 캡슐화 하기로 한거다. 이렇게 된다면 데이터에 대한 안정성이 높아지는 장점이 있다.
결국, 모르는 것이 access를 확실하게 못하는 방식이었으므로, 변수와 함수에 대해서 클래스라는 벽을 친것이다. 근데 그럴꺼면 왜? 같은 파일 내에 여러개의 클래스를 두어야 되는가? 결국, 하나의 목표를 위해서 사용될 클래스들이 아닌가? 어떻게든 연관성이 있지 않을까? 그러면 연관성은 있되, 일부만 오픈할 수는 없을까? 라는 문제점때문에 private , public 개념이 나오게 되었다.
따라서 클래스 간에는 상호연결이 가능하므로 최소한의 공개 원칙으로 하게 해준다.
private / public 권장 사항
#include <iostream>
using namespace std;
class Negative
{
};
class Sample
{
int n;
public:
int m;
void setN(int x)
{
if (x < 0)
throw Negative();
n = x;
}
int getN()
{
return n;
}
};
int main()
{
Sample x, y;
x.setN(1);
y.setN(2);
cout << "x.n: " << x.getN() << endl;
cout << "y.n: " << y.getN() << endl;
x.m = 10;
y.m = 20;
cout << "x.m: " << x.m << endl;
cout << "y.m: " << y.m << endl;
}
>>>
x.n: 1
y.n: 2
x.m: 10
y.m: 20
클래스 내의 n은 private이었는데 main에서 어떻게 사용을 해야되냐면 public을 통해서 변수를 설정할 수 있는 함수를 만들어서 하는 방식을 권장한다.
왜냐하면 public된 멤버 함수를 통해서 변수에 대한 범위를 설정하여서 한 번더 변수에 대해서 검사를 해서 우회적으로 값을 설정하는 방법을 권장한다.
constructor
얘는 스페셜 함수인데, 왜냐하면 함수의 이름이 고정되어 있기 때문이다. constructor는 항상 클래스의 이름과 동일하다.
class Sample
{
private:
int m;
public:
Sample() {
m = 0;
n = 0;
}
int n;
void setM(int x)
{
if (x < 0)
throw Negative();
m = x;
}
int getM()
{
return m;
}
};
constructor가 특별한 이유는 해당 오브젝트가 만들어질 때 그게 디폴트로 불리게 된다. 그 인스턴스가 최초의 정의가 될 때 그때 자동으로 한 번 호출이 된다. public에 Sample이라는 함수가 있다고 해서 맘대로 호출할 수 있는 함수가 아니다. 또한 호출하고 싶지 않아도 인스턴스가 생성될 때 자동으로 호출되는 함수이다.
따라서 기본적으로 생성자는 public이며 다형성의 원리로 여러 생성자를 오버로딩 가능하다.
-
근데 클래스를 만들 때, 생성자를 생성해주지 않으면 어떻게 불러와?
생성자가 한 개도 없으면 default 생성자를 만들게 된다. 하지만 최소 한 개의 생성자가 있으면 더이상 default 생성자를 만들어 주지는 않는다.
인스턴스는 얼마만큼의 메모리를 차지할까?
멤버 변수만큼 잡아먹는다. 왜냐하면 함수는 값을 조정하는 것이 아니기 때문에 함수는 모든 인스턴스가 공유하는 값이 되고, 변수들만 메모리를 잡아먹게 된다.
- 멤버 함수: 메모리를 별도로 잡지 X
- 멤버 변수: 메모리를 별도로 잡아
.cpp, .h
그럼 .h와 .cpp를 분리하는 이유는 무엇일까?
.h의 경우는 high lang으로 구성되어 있다. 오픈 소스 이전에 상황에서 봤을 때, 자기가 짠 클래스가 어떤 멤버 변수를 가지고 있고, 어떤 멤버 함수를 가지고 있는지 보여주어야 되기 때문이다. 하지만 그 안에 멤버 함수를 어떻게 짰는지는 공개하기 싫었기 때문에 파일을 분리하여 .h는 high lang으로 .cpp로는 .h 파일은 클래스를 정의하는 내용을 구성되어있다.
- .h파일 : 멤버 변수 정의 및 멤버 함수 선언
- .cpp파일 : 멤버 함수 정의
// test.h
// high lang으로 오픈
#ifndef __TEST_H
#define __TEST_H
class Sample
{
int n;
public:
Sample();
Sample(int x);
void setN(int x);
int setN();
};
#endif
// test.cpp
// test.h의 멤버 함수를 정의
#include <iostream>
#include "test.h"
using namespace std;
Sample::Sample()
{
n = 0;
cout << "Sample() is Called" << endl;
}
Sample::Sample(int x)
{
n = x;
cout << "Sample() is Called" << endl;
}
void Sample::setN(int x)
{
n = x;
}
int Sample::setN()
{
return n;
}
// test_main.cpp
// 사용
#include "test.h"
#include <iostream>
using namespace std;
int main()
{
Sample x, y;
Sample z(10);
x.setN(1);
y.setN(2);
cout << "x.n: " << x.setN() << endl;
cout << "y.n: " << y.setN() << endl;
}
>>>
Sample() is Called
Sample() is Called
Sample() is Called
x.n: 1
y.n: 2
댓글남기기