월 24,200원
5개월 할부 시다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결홍정모의 따라하며 배우는 C++
이거 그냥 []연산자 오버로딩이 맞나요?
(사진)
- 해결됨홍정모의 따라하며 배우는 C++
return *this에 관해 질문이 있습니다.
*this가 멤버변수를 참조한다는 것은 알겠는데 왜 그냥 this를 리턴하면 저 오류가 뜨는지 모르겠습니다.this도 인스턴스화된 객체를 가리키는 포인터니까 좌값이 맞지 않나요? 아니면 인스턴스의 주소가 우값에 속해있는 건가요??
- 미해결홍정모의 따라하며 배우는 C++
안녕하세요 위임 생성자 강의를 듣는 도중에 궁금한 점이 생겼습니다.
위임 생성자가 매개변수에 값을 입력 안했을 때, 클래스를 다시 참조해서 사용자가 입력한 값을 인스턴스에서 출력하는 것이잖아요?? 가령, Student(const string& name_in) : Student(0, name_in) {} 와 같이요.그런데 잘 생각해보면 디폴트 매개변수 생성자도 매개변수에 미리 값을 설정해주고, 인스턴스 인자에 값을 안넣어주면 디폴트값을 출력하는 것도 같은 원리인데 차이점이 무엇인가요??
- 미해결홍정모의 따라하며 배우는 C++
메모리가 새고 있는 지 확인하는 방법
while (true) { int* ptr = new int; cout << ptr << endl; } // 메모리가 새고 있는 지 확인법 // 1. Task manager에서 메모리 사용량이 무한히 올라가면 메모리가 새고 있는 것 // 2. ..? 메모리가 새고 있는 지 확인하는 방법은 2가지가 있다고 하셨는데 제가 캐치를 못했는지 하나밖에 모르겠어요ㅠㅠ 다른 방법은 무엇이 있나요?
- 미해결홍정모의 따라하며 배우는 C++
delete 후 주소값
int *ptr = new int (7); cout << ptr << endl; // 000001879A455E70 cout << *ptr << endl; // 7delete ptr; cout << ptr << endl; //0000000000008123 // 강의 내에서는 위의 ptr과 아래의 ptr이 같은 주소값으로 출력되는데 저는 안그렇습니다. 왜이런걸까요?
- 미해결홍정모의 따라하며 배우는 C++
컴파일시 exe 파일이 생성이 안되네요
강의에서는 코드 작성 후 실행을 누르면 exe파일이 생성된 경로로 cmd창에서 이동 후 명령어로 코드를 실행시키는데저는 실행을 눌러도 exe파일이 생성이 안되고 exe.recipe라는 파일만 보이는데 혹시 설정을 다르게 해야하나요?그리고 혹시 컴파일과 빌드의 용어의 차이점을 설명해주실수 있으실까요? 강의에서는 cmd창을 비쥬얼스튜디오용? cmd창을 사용하는것 같은데 그냥 윈도우에서 cmd검색시 나오는 기본 명령프롬프트와는 어떻게 다른건가요?
- 미해결홍정모의 따라하며 배우는 C++
문자열내의 개행문자 출력방법
위 코드를 실행시 아래와 같은 결과가 나왔습니다.강의 예제처럼 n_name의 값은 10이 나옵니다.1,2는 예상대로 나왔는데, 3,4,5는 강의예제와 달리 개행문자가 출력이 되지 않았습니다.visual studio 2022 community를 사용하였는데, 개행문자는 출력하지 않는 옵션이 있는 건가요?
- 미해결홍정모의 따라하며 배우는 C++
for (const auto &number : fibonacci), &유무의 차이점?
int main(){ int fibonacci[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 }; for (auto& number : fibonacci) number *= 10; //for (const auto number : fibonacci) for (const auto &number : fibonacci) cout << number << " "; // -------1) cout << endl; return 0;}1) 라인에서 number 값을 변경없이 출력만 하고 있습니다.이 경우 for문에서 number와 &number의 차이가 있을까요? 두 경우 모두 fibonacci 배열의 값을 변경하지 않으면 그냥 number를 쓰는 것이 나을까요?
- 미해결홍정모의 따라하며 배우는 C++
클래스의 멤버함수에 대해
같은 클래스로 만들어진 인스턴스들은 해당 클래스의 멤버함수가 담긴 하나의 메모리만을 사용한다고 이해해도 되나요? 각자의 함수를 따로따로 메모리에 담고 있지는 않는다고 앞에서는 설명해주셨던거 같은데 이번강의에서도 개념은 똑같은것 같아서요
- 미해결홍정모의 따라하며 배우는 C++
언리얼엔진 c++에서
언리얼엔진에서 한 클래스 안에 public privatepublic 이런식으로 퍼블릭나왔다 프라이빗 나왔다가 퍼블릭 나왔다가 이렇게 되어 있는 경우를 봤는데 다를게 없는걸로 봐도 되죠?
- 미해결홍정모의 따라하며 배우는 C++
인스턴스 객체 클래스 오브젝트 정리
데이터와 기능이 묶여있는 것이 Object이고 object랑 객체는 같은 말이고 이런 오브젝트종류중에 하나가 클래스이며 클래스로 오브젝트를 찍어낼 수 있고 이렇게 찍어낸 것들이 인스턴스인게 맞나요? 용어 정리를 명확히 하고 싶네요!
- 미해결홍정모의 따라하며 배우는 C++
12.7 순수 가상 함수
강의가 잘 이해가 안가는데요이번 강의는 이해되는거 같으면서도 좀 애매하네요 ㅠclass IErrorLog {public:virtual bool reportError(const char *errorMessage) = 0;virtual ~IErrorLog(){}}; void doSomething(IErrorLog &log){log.reportError("runtime error!!");} doSomething에서 runtime error이거는 왜 쓴건가요?? 아무 말이나 써도 영향을 안주는 거 같아서요 어차피 dosomthing의 매개변수가 추상클래스 (IErrorLog)면 reportError 매개변수로 뭘 써놔도 의미없는게 맞나요?? 아니면 다른 용도가 있는 건가요? 디버깅에서는 그냥 들어갔다가 derivedclass의 override된 함수의 매개변수가 들어가면서 사라지던데요
- 해결됨홍정모의 따라하며 배우는 C++
형변화 오버로딩을 어떤식으로 이해해야 할지모르겠습니다.
안녕하세요. 9.8 형 변환 강의를 듣던 중 이해가 되지 않는 부분이 있어서 질문합니다.19~23줄에 있는 int형 변환 함수에 리턴타입이 없는이유가 궁금합니다.함수가 클래스 내부변수에 이미 멤버로 정의가 되어있어서 변수에 접근할 수 있기 때문에 따로 리턴타입을 정의하지 않은건가요? 19줄 operator int 뒤에 있는 ()의 의미가 궁금합니다. 예를 들어 아래는 9-6 수업 첨자연산자 오버로딩의 예 입니다.int& operator [] (const int index) { return m_list[index]; }그런데 이경우는 매개변수도 정의가 되어있고 리턴타입도 정의가 되어있어서 [ ] 안에 값을 넣음으로서 이러한 기능이 작동하는것으로 알고있었습니다. 그런데 19줄은 매개변수가 없어서 int뒤에오는 ()가 매개변수를 담는 ()인지 아니면 위 예에서 [ ]같이 사용되는것인지 모르겠습니다. [ ] 처럼사용되는것이라면 47줄에는 ( )안에 Cents가 아닌 int가 들어가있는데 어째서 정상작동하는것인지 모르겠습니다.앞서 1,2번의 궁금증을 해결하기보다는 그냥 오버로딩이 이러한 형식으로 작동하고 47~49줄 처럼 형태는 다르지만 오버로딩을 배우기전 알고있던 기본적인 형변환방식에 모두 적용이된다 라고만 이해하고있으면 될까요?
- 미해결홍정모의 따라하며 배우는 C++
파라미터로 넣은 참조를 함수가 바꾸지 못하게 막는건
목적이 파라미터로 넣은 값은 그대로 유지 하면서 함수의 return값도 따로 받고 싶어서 그러는 건가요? return값도 사용하고 파라미터로 넣은 참조값도 그대로 사용하고 싶어서??
- 해결됨홍정모의 따라하며 배우는 C++
9:23부터 리턴값을 자기자신의 래퍼런스를 지정한다고 들었는데 정확히 *this가 어떤 타입인지 모르겠습니다.
9:23 부터 리턴값으로 인스턴스의 래퍼런스를 지정한다고 이해했습니다.그러면 *this는 정확히 어떤타입인건가요?*는 포인터에서 가리키는 주소의 값에 접근할수있는 연산자라고 이해했습니다. this가 인스턴스의 주소를 가리키는 포인터이니 *this는 Calc 클래스내부의 변수 m_value의 값인 건가요? 그렇다면 Calc&는 int형을 반환하는 리턴타입이라고 봐도 무방할까요?
- 미해결홍정모의 따라하며 배우는 C++
6.3 배열과 반복문 const 관련 질문입니다.
다른 질문에서 배열의 크기는 const 상수로 컴파일 크기를 통해서 정해줘야한다는 답변은 이해했습니다!그런데 int scores[] = { ...}; 는 올바른 형식이잖아요?그렇다면 배열의 크기는 알아서 설정이 되었다고 생각됩니다.그래서, 여기서 num_students에 const를 하신 이유가 궁금합니다.여기서의 num_students는 배열 정의시에 들어가지 않고 for문에서 단순 조건문에 들어갈 뿐인데, const를 한 이유가 궁금합니다!. 제가 cosnt를 빼고 실행했을때도 실행이 되더라구요!.처음에 배열 사이즈 변수로 넣으려고 할 때 const가 붙여있어서 그랬던 것이고, 나중에 다른 케이스 변경하면서 굳이 지우지 않았던 것인가요?감사합니다.
- 미해결홍정모의 따라하며 배우는 C++
06:55질문
06:55 에서 static const double pi=3.141592;를 헤더로 따로 나눈다고 하셨는데요그럼 해당 헤더파일을 사용하는 cpp파일마다 해당 변수의 메모리를 따로 만들어지니까 별로 안좋지 않나요??
- 미해결홍정모의 따라하며 배우는 C++
3이 아니구요?
3:58 초 부근에서 foo(x,y+1);인데 y=2이니까 2+1해서 3이 전달되는거 아닌가요????
- 미해결홍정모의 따라하며 배우는 C++
27분 05초 부근 질문있습니다.
divide & conquer 전략으로 lockguard를 쓰는 thread, atomic 쓰는 thread, promise 쓰는 thread 이렇게 만드는 건데 애초에 divide & conquer 전략이 무엇인지도 잘 모르겠고 코드를 배운대로 짠 거 같은데 자꾸 오류가 나서 막막합니다. 도움을 조금 주시면 감사하겠습니다. 밑에는 제가 짠 코드입니다.// 여태까지 멀티 스레딩을 사용하는 여러가지 문법들을 배워봤다. // 이번에는 멀티 스레딩으로 벡터 내적을 계산하는 예제를 통해서 실제 문법이 어떻게 사용이 되는 지 그리고 차이가 어떻게 나는 지 등등을 한 번 직접 실험하면서 살펴본다. #include <iostream> #include <chrono> #include <mutex> #include <random> #include <thread> #include <utility> #include <vector> #include <atomic> #include <future> #include <numeric> // std::inner_product #include <execution> // parallel execution using namespace std; mutex mtx; // mutex를 꼭 전역 변수로 선언할 필요는 없다. 여러 개의 스레드가 공통적으로 접근할 수 있는 scope에 있으면 된다. // 그리고 mutex도 항상 존재할 필요는 없다. mutex로 lockguard를 만들어주고 싶은 부분이 작동할 때만 mutex가 살아있으면 된다. // 그러니깐 코딩할 때 mutex를 어떤 클래스의 멤버로 집어넣어도 되고 scope에 넣어도 된다. void dotProductNaive(const vector<int>& v0, const vector<int>& v1, const unsigned i_start, const unsigned i_end, unsigned long long& sum) { for (unsigned i = i_start; i < i_end; ++i) sum += v0[i] * v1[i]; } void dotProductLock(const vector<int>& v0, const vector<int>& v1, const unsigned i_start, const unsigned i_end, unsigned long long& sum) { //std::scoped_lock lock(mtx); // 속도를 높이기 위해 scoped_lock을 for문 밖으로 옮기면 어떨까라고 생각할 수 있다. // scoped_lock이 작동하는 범위를 scope 전체가 되도록 옮기니깐 속도가 오히려 훨씬 빨라졌다. // 그러면 오히려 밖으로 빼면 되나 생각할 수 있는데 지금 cout으로 출력된 값을 보니깐 start, end, start, end, ... 이렇게 나왔다. // 이 전체가 lock이기 때문에 병렬이 아니라, 동시적으로 실행이 된 게 아니라 순차적으로 실행이 됐다는 의미이다. // 실제로 이런 방식으로 병렬처리를 하는 것은 의미가 없다는 거다. // 오히려 스레드를 생성하는 오버헤드만 커지게 된다. cout << "Thread start " << endl; for (unsigned i = i_start; i < i_end; ++i) { std::scoped_lock lock(mtx); // C++ 17 // 초반에 말했었지만 scoped_lock은 영역이 작은 쪽에 들어가는게 좋은데 작은 영역에 들어오긴 했지만 너무 빈번하게 자주 실행이 되어버리면 오히려 느려진다. // 멀티 스레딩을 안 하느니만 못하는 최악의 결과가 나오게 된다. 그래서 멀티 스레딩을 처음 시도를 하는 경우에는 오히려 퍼포먼스가 떨어지거나 혹은 퍼포먼스는 안 떨어지는 대신에 결과가 나쁘거나 이런 식으로 작동이 될 수 있다. // 그런데 이것도 경우에 따라서 또 다르다. 락이 걸린 scope 전후로 어마어마하게 일이 많으면 상대적으로 멀티 스레딩의 효율이 높아지기도 하는데 일단은 극단적인 경우를 가지고 실수를 할 수 있는 부분에 대해서 먼저 설명을 했다. sum += v0[i] * v1[i]; } cout << "Thread end " << endl; } void dotProductAtomic(const vector<int>& v0, const vector<int>& v1, const unsigned i_start, const unsigned i_end, atomic<unsigned long long>& sum) { for (unsigned i = i_start; i < i_end; ++i) { sum += v0[i] * v1[i]; // atomic 연산이 일반연산보다 느리기 때문에 빈번하게 호출되는 곳에 atomic 연산을 넣어버리면 느려진다. } } auto dotProductFuture(const vector<int>& v0, const vector<int>& v1, const unsigned i_start, const unsigned i_end) { int sum = 0; // local sum for (unsigned i = i_start; i < i_end; ++i) { sum += v0[i] * v1[i]; } return sum; } int main() { /* v0 = { 1, 2, 3 } v1 = { 4, 5, 6 } v0_dot_v1 = 1*4 + 2*5 + 3*6; */ const long long n_data = 100'000'000; const unsigned n_threads = 4; /* initialize vectors */ std::vector<int> v0, v1; v0.reserve(n_data); v1.reserve(n_data); // reserve()로 push할 때의 효율을 높였다. random_device seed; mt19937 engine(seed()); uniform_int_distribution<> uniformDist(1, 10); for (long long i = 0; i < n_data; ++i) { v0.push_back(uniformDist(engine)); v1.push_back(uniformDist(engine)); } cout << "std::inner_product" << endl; { const auto sta = chrono::steady_clock::now(); const auto sum = std::inner_product(v0.begin(), v0.end(), v1.begin(), 0ull); // auto인데도 unsigned long long으로 자동 추론을 할 수 있게 된 이유는 0을 0ull으로 표기를 해서 넣어줬기 때문이다. const chrono::duration<double> dur = chrono::steady_clock::now() - sta; cout << dur.count() << endl; cout << sum << endl; cout << endl; } // 멀티 스레딩 프로그래밍을 한다면 정답을 계산을 하고 그것과 정답이 맞는지 비교하고 그리고 퍼포먼스를 또 비교하는 방식으로 작업을 하는 것이 좋다. cout << "Naive" << endl; { const auto sta = chrono::steady_clock::now(); unsigned long long sum = 0; vector<thread> threads; threads.resize(n_threads); const unsigned n_per_thread = n_data / n_threads; // assumes remainder = 0 for (unsigned t = 0; t < n_threads; ++t) threads[t] = std::thread(dotProductNaive, std::ref(v0), std::ref(v1), t * n_per_thread, (t + 1) * n_per_thread, std::ref(sum)); for (unsigned t = 0; t < n_threads; ++t) threads[t].join(); const chrono::duration<double> dur = chrono::steady_clock::now() - sta; cout << dur.count() << endl; cout << sum << endl; cout << endl; } // 스레드 개수를 임의의 개수로 사용할 수 있게 하기 위해서 조금 코드가 복잡해졌다. // 실행해보면 시간도 더 걸렸고 무엇보다 답이 틀리게 나온다. 레이스 컨디션 때문에 그렇다. // sum에다가 값을 더하고 있는데 여러 개의 스레드가 동시에 값을 건드리니깐 문제가 생겨서 값이 제대로 더해지지 않고 있다. cout << "Lockguard" << endl; { const auto sta = chrono::steady_clock::now(); unsigned long long sum = 0; vector<thread> threads; threads.resize(n_threads); const unsigned n_per_thread = n_data / n_threads; // assumes remainder = 0 for (unsigned t = 0; t < n_threads; ++t) threads[t] = std::thread(dotProductLock, std::ref(v0), std::ref(v1), t * n_per_thread, (t + 1) * n_per_thread, std::ref(sum)); for (unsigned t = 0; t < n_threads; ++t) threads[t].join(); const chrono::duration<double> dur = chrono::steady_clock::now() - sta; cout << dur.count() << endl; cout << sum << endl; cout << endl; } cout << "Atomic" << endl; { const auto sta = chrono::steady_clock::now(); atomic<unsigned long long> sum = 0; vector<thread> threads; threads.resize(n_threads); const unsigned n_per_thread = n_data / n_threads; // assumes remainder = 0 for (unsigned t = 0; t < n_threads; ++t) threads[t] = std::thread(dotProductAtomic, std::ref(v0), std::ref(v1), t * n_per_thread, (t + 1) * n_per_thread, std::ref(sum)); for (unsigned t = 0; t < n_threads; ++t) threads[t].join(); const chrono::duration<double> dur = chrono::steady_clock::now() - sta; cout << dur.count() << endl; cout << sum << endl; cout << endl; } cout << "Future" << endl; { const auto sta = chrono::steady_clock::now(); unsigned long long sum = 0; vector<std::future<int>> futures; // unsigned long long으로 받지 않은 이유는 부분의 합은 integer 범위 안에 들어오기 때문이다. futures.resize(n_threads); const unsigned n_per_thread = n_data / n_threads; // assumes remainder = 0 for (unsigned t = 0; t < n_threads; ++t) futures[t] = std::async(dotProductFuture, std::ref(v0), std::ref(v1), t * n_per_thread, (t + 1) * n_per_thread); // 전역 변수 역할을 하는 sum이 들어갈 필요가 없다. 왜냐하면 dotProductFuture()에서는 local sum을 계산해서 return 해주고 있기 때문이다. for (unsigned t = 0; t < n_threads; ++t) sum += futures[t].get(); // .join()을 하는 대신에 .get()을 받아서 sum에다가 더해주고 있다. const chrono::duration<double> dur = chrono::steady_clock::now() - sta; cout << dur.count() << endl; cout << sum << endl; cout << endl; } // future 쓰는게 무조건 좋구나 thread 나빠 이렇게 생각할 수도 있다. 그런데 꼭 그렇진 않다. //TODO: use divide and conquer strategy for std::thread, std::thread & std::atomic //TODO: use divide and conquer strategy for std::thread & std::promise /* divide and conquer strategy for lockguard */ cout << "Divide Lockguard" << endl; { const auto sta = chrono::steady_clock::now(); vector<unsigned long long> divide_sum; divide_sum.resize(n_threads); vector<thread> threads; threads.resize(n_threads); const unsigned n_per_thread = n_data / n_threads; // assumes remainder = 0 for (unsigned t = 0; t < n_threads; ++t) threads[t] = std::thread(dotProductLock, std::ref(v0), std::ref(v1), t * n_per_thread, (t + 1) * n_per_thread, std::ref(divide_sum[t])); for (unsigned t = 0; t < n_threads; ++t) threads[t].join(); unsigned long long sum = 0; for (unsigned t = 0; t < n_threads; ++t) sum += divide_sum[t]; const chrono::duration<double> dur = chrono::steady_clock::now() - sta; cout << dur.count() << endl; cout << sum << endl; cout << endl; } /* divide and conquer strategy for atomic */ cout << "Divide Atomic" << endl; { const auto sta = chrono::steady_clock::now(); vector<atomic<unsigned long long>> divide_sum; divide_sum.resize(n_threads); vector<thread> threads; threads.resize(n_threads); const unsigned n_per_thread = n_data / n_threads; // assumes remainder = 0 for (unsigned t = 0; t < n_threads; ++t) threads[t] = std::thread(dotProductAtomic, std::ref(v0), std::ref(v1), t * n_per_thread, (t + 1) * n_per_thread, std::ref(divide_sum[t])); for (unsigned t = 0; t < n_threads; ++t) threads[t].join(); unsigned long long sum = 0; for (unsigned t = 0; t < n_threads; ++t) sum += divide_sum[t]; const chrono::duration<double> dur = chrono::steady_clock::now() - sta; cout << dur.count() << endl; cout << sum << endl; cout << endl; } /* divide and conquer strategy for promise */ cout << "Promise" << endl; { const auto sta = chrono::steady_clock::now(); unsigned long long sum = 0; vector<std::promise<int>> proms; proms.resize(n_threads); vector<std::future<int>> futures; futures.resize(n_threads); vector<thread> threads; threads.resize(n_threads); const unsigned n_per_thread = n_data / n_threads; // assumes remainder = 0 for (unsigned t = 0; t < n_threads; ++t) { futures[t] = proms[t].get_future(); threads[t] = std::thread([&](std::promise<int>&& prom) { prom.set_value(dotProductFuture(std::ref(v0), std::ref(v1), t* n_per_thread, (t + 1)* n_per_thread)); }, std::move(proms[t])); } for (unsigned t = 0; t < n_threads; ++t) sum += futures[t].get(); for (unsigned t = 0; t < n_threads; ++t) threads[t].join(); const chrono::duration<double> dur = chrono::steady_clock::now() - sta; cout << dur.count() << endl; cout << sum << endl; cout << endl; } cout << "std::transform_reduce" << endl; { const auto sta = chrono::steady_clock::now(); //const auto sum = std::transform_reduce(std::execution::seq, v0.begin(), v0.end(), v1.begin(), 0ull); const auto sum = std::transform_reduce(std::execution::par, v0.begin(), v0.end(), v1.begin(), 0ull); const chrono::duration<double> dur = chrono::steady_clock::now() - sta; cout << dur.count() << endl; cout << sum << endl; cout << endl; } // std::inner_product의 병렬 버전이 std::transform_reduce()이다. std::transform_reduce() 말고도 std::sort() 같은 함수들이 전부 병렬 처리를 지원해준다. 그래서 기본적인 것들을 빠르게 구현할 때는 병렬 처리를 직접 구현할 필요는 없다. // 하지만 병렬 처리 함수들을 사용하기에 앞서 멀티 스레딩을 먼저 조금 알고 있으면 사용할 때 조금 더 유리할 거고 둘째로 std에서 만들어준 코드들이 모든 문제들을 전부 병렬 처리 해주지는 않는다. 그리고 여전히 병렬 처리했을 때 문제가 생길 여지가 분명히 존재한다. return 0; }
- 미해결홍정모의 따라하며 배우는 C++
정리
제가 이해한게 맞을까요?