어떻게 하면 코드 작성자가 사용자에게 더 편의를 제공할 것인가?

C에서는 structure의 멤버들에 대한 정보/사용법을 사용자가 모두 알아야만 한다.

#include <stdio.h>

//code writer
typedef struct USERDATA
{
	int nAge;
	char szName[32];
	void(*Print)(struct USERDATA *);	//step 3 improvement
} USERDATA;
 
void PrintData(USERDATA *pUser)			//step 2 improvement
{
	printf("%d, %s\n", pUser->nAge, pUser->szName);
}

// code user
int main(void)
{
	USERDATA user = { 20, "Peter", PrintData };
	//printf("%d, %s\n", user.nAge, user.szName);		//step 1 C style
	//PrintData(&user);					//step 2 improvement
	user.Print(&user);					//step 3 improvement
    
	return 0;
}

From step 3, it seems the function is working as structure's member in code user point of view.

But it looks redundant which is using 'user' stucture's address agin from Print member function.

// code user
int main(void)
{
	USERDATA user = { 20, "Peter", PrintData };
    //printf("%d, %s\n", user.nAge, user.szName);	//step 1 C style
    //PrintData(&user);					//step 2 improvement
    //user.Print(&user);				//step 3 improvement
    user.Print();					//step 4 improvement
    
	return 0;
}

In C++, it's decided to hide &user parameter. it's related with 'this' pointer.

 

#include "stdafx.h"
#include <iostream>
using namespace std;

// code writer
class CTest
{
public :
	CTest() {}
    
	int m_nData1 = 10;
	int m_nData2 = 20;
    
	void PrintData(void)
	{
		cout << m_nData1 << endl;
		cout << m_nData2 << endl;
	}
};

// user code
int _tmain(int argc, _TCHAR* argv[])
{
	CTest t;
	t.PrintData();

	return 0;
}
반응형

'Prog&Algol' 카테고리의 다른 글

Algo | C++ | DFS and BFS  (0) 2022.03.30
BJ | 2178 | BFS shorted path  (0) 2022.03.30
C++ | Namespace  (0) 2022.03.29
BJ | 9996 | connect server when missing Korea  (0) 2022.03.29
BJ | 2309 | 일곱 난쟁이 | Combination  (0) 2022.03.22

2.4.1 Namespace

C++가 지원하는 각종 요소들(변수, 함수, 클래스 등)을 한 범주로 묶어주기 위한 문법

소속이나 구역이라는 개념

 

#include "stdafx.h"
#include <iostream>

namespace TEST
{
	int g_nData = 100;
    
	void TestFunc(void)
	{
		std::cout << "TEST::TestFunc()" << std::endl;
	}
}
 
// _tmain은 Global namespace 소속
int _tmain(int argc, _TCHAR* argv[])
{
	TEST::TestFunc();
	// cout은 std 네임스페이스 소속
	std::cout << TEST::g_nData << std::endl;
    
	return 0;
}

 

2.4.2 Using

네임스페이스를 임의로 생략

#include "stdafx.h"
#include <iostream>

// declare std namespace with 'using' keyword
using namespace std;

namespace TEST
{
	int g_nData = 100;
    
	void TestFunc(void)
	{
		cout << "TEST::TestFunc()" << endl;
	}
}

// declare TEST namespace with 'using' keyword
using namespace TEST;

int _tmain(int argc, _TCHAR* argv[])
{
	TestFunc();
	cout << g_nData << endl;
    
	return 0;
}

 

2.4.3 nested Namespace

네임스페이스 안에 또 다른 네임스페이스가 속할 수 있음

#include "stdafx.h"
#include <iostream>

using namespace std;

namespace TEST
{
	int g_nData = 100;
    namespace DEV
    {
    	int g_nData = 200;
        namespace WIN
		{
        	int g_nData = 300;
		}
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	cout << TEST::g_nData << endl;
    cout << TEST::DEV::g_nData << endl;
    cout << TEST::DEV:WIN::g_nData << endl;
    
    return 0;
}

2.4.4 Namespace의 다중정의

#include "stdafx.h"
#include <iostream>

using namespace std;

//global
void TestFunc(void) { cout << "::TestFunc()" << endl;

namespace TEST
{
	void TestFunc(void){
    	cout << "TEST:TestFunc()" << endl;
	}
}

namespace MYDATA
{
	void TestFunc(void){
    	cout << "MYDATA::TestFunc()" << endl;
	}
}

int _tmain(int argc, _TCHAR* argv[])
{

	TestFunc();	//implicitly global
    ::TestFunc();	//explicitly global
    TEST::TestFunc();
    MYDATA::TestFunc();
    
    return 0;
}

첫번째 TestFunc()은 별도의 Namespace를 지정하지 않았는데, _tmain()함수의 namespace가 전역이기 때문이다.

 

int TestFunc(int);
int Data::TestFunc(int);
int CMyData::TestFunc(int);
int CMyData::TestFunc(int) const;

1번은 소속이 없음, 2~3번은 속해 있는 namespace나 class가 존재함, 4번은 CMyData라는 클래스의 상수형 Method임

 

2.5.4 using 선언과 전역 변수

#include "stdafx.h"
#include <iostream>

using namespace std;

namespace TEST
{
	int nData = 200;
}

using namespace TEST;

int _tmain(int argc, _TCHAR* argv[])
{
	cout << nData << endl;
    
    return 0;
}

위의 예제에서 컴파일 에러가 발생함. nData가 전역 네임스페이스일 수도 TEST 네인스페이스일수도 있음

::nData라고 범위 식별자를 기술하거나, TEST::nData라고 정확하게 네임스페이스를 기술해야 함

반응형

'Prog&Algol' 카테고리의 다른 글

BJ | 2178 | BFS shorted path  (0) 2022.03.30
C++ | history of Class  (0) 2022.03.30
BJ | 9996 | connect server when missing Korea  (0) 2022.03.29
BJ | 2309 | 일곱 난쟁이 | Combination  (0) 2022.03.22
C++ | lower_bound & upper_bound  (0) 2022.03.22

변수를 초기화할 때, 명시적 타입을 적는 대신 auto 타입 지정자를 넣을 수 있다. auto는 그것의 초기자로부터 개체의 타입을 추론하게 되는데, 타입은 변수, const나 constexpr일 수 있다. 하지만 구문의 타입은 레퍼런스가 될 수 없다. 왜냐하면 레퍼런스는 암시적으로 구문에서 참조되지 않기 때문이다.

다시 말해, auto는 초기자의 타입을 위한 플레이스홀더(placeholder)가 된다.

#include <iostream>
#include <typeinfo>

using namespace std;

int main() {
	auto sum = 1 + 3;
	cout << sum << endl;
	cout << typeid(sum).name() << endl;
	return 0;
}
  • c++ 11에서 타입 추론.
  • auto 키워드는 선언된 변수의 초기화 식을 사용하여 해당 형식을 추론하도록 컴파일러에 지시한다.
  • auto 키워드를 사용하면 초깃값의 형식에 맞춰 선언하는 인스턴스(변수)의 형식이 자동으로 결정된다. 이것을 타입추론(type inference)라고 한다.
  • 단 auto 사용에는 주의해야할 점이 있는데 생성 시 변수를 초기화 할때만 작동한다. 초기화 값을 사용하지 않고 생성된 변수는 이 기능을 사용할 수 없다.

 

  • 또한 auto 키워드는 함수 매개 변수와 함께 사용할 수 없다.

range-based for statement

 

#include <iostream> 
using namespace std;

int main() 
{ 
	int fibonacci[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 };
	for (int number : fibonacci)  cout << number << ' '; 

return 0; 
}

element_declaration은 배열 요소와 같은 자료형을 가져야 하므로, auto keyword를 사용해서 C++가 자료형을 추론하도록 하는것도 좋은 방법이다.

#include <iostream>
using namespace std;

int main() {
	int arr[5];
	for (int i = 0; i < 5; i++) arr[i] = i;
	for (auto num : arr) cout << num << ' ';
	return 0;
}

 

위의 경우 각 배열의 요소가 num으로 복사가 된다. 

#include <iostream>
using namespace std;

int main() {
	int arr[5];
	for (int i = 0; i < 5; i++) arr[i] = i;
	for (auto &num : arr) cout << num << ' '; //using reference
	return 0;
}

위의 경우 num은 현재 반복된 배열 요소에 대한 참조이므로 값이 복사되지 않지만, num을 수정하면 배열의 요소에 영향을 준다. 이런 경우 읽기 전용인 const로 num을 만들어 볼 수 있다.

#include <iostream>
using namespace std;

int main() {
	int arr[5];
	for (int i = 0; i < 5; i++) arr[i] = i;
	for (const auto &num : arr) cout << num << ' ';
	return 0;
}
반응형

'Prog&Algol' 카테고리의 다른 글

Algo | C++ | priority Queue  (0) 2022.03.20
C++ | Range-based for loops  (0) 2022.03.20
C++ | 초기화 리스트/initialize_list | after c++11  (0) 2022.03.20
C++ | using namespace  (0) 2022.03.17
2019 Winter Kakao Internship - Hotel room  (0) 2020.04.14

 

C++98 에서는 Member initializer list (콜론 초기화)를 통해서 멤버 변수 초기화 가능하다. 

C++11 부터는 Braced-Init-List를 지원. C++11 이전의 문법에서 멤버 변수의 초기화가 불가능 했다.

클래스의 정의는 실제 메모리에 인스턴스를 생성하는 작업이 아니기 때문이다. 단 static 으로 선한한 멤버 변수는 초기화 가능하다.

 

컨테이너 및 STL에는 std::initializer_list<>로 구현되어 있다.

 

1. 멤버 데이터를 초기화는 3가지 방법

#include <ioostream>
 
class Point {
    int x = 0; // Case 1 : after C++11
    int y = 0;
public:
    Point(int a, int b) : x(a), y(b) {	// Case 2
        x = a;	// Case 3
        y = b;
    }
};
 
int main() {
    Point p(1, 2);
}

1. member field initialization : Can not initialize with the values from Constructor.

2. member initializer list : It's initialization, not assignment.

3. Initilize from inside of Constructor : It's assignment, not initialization.

 

 

#include <iostream>

class Point {
	int x;
    int y;
    
public :
	Point (int a, int b) : x(a), y(b) {}  // Member initializer list
};

int main(){
	Point p(1,2);
    class a = 0; // Initialize first, then call Constructor
    
    class b; // call Constructor
    b=0;	 // assignment
}

멤버 초기화 리스트를 사용하면 생성자 코드를 간단히 나타낼 수 있다. 또한 대입이 아니기기에 효율적으로 초기화가 가능하다. 멤버 이니셜라이져를 통한 초기화는 객체의 생성 이전에 이루어진다.

 

2. 꼭 멤버 초기화 리스트를 사용하는 예

1. 클래스 안에 멤버 데이터가 const 또는 reference로 되어 있을때

#include <iostream>
using namespace std;

class point {
	private :
    	const int temp=0; // Error
        int x;
        int y;
        
	public :
    	point(int _x, int _y) : x(_x), y(_y) {}
        void print() {
        	cout << 'x' << ':' << x << endl;
            cout << 'y' << ':' << y << endl;
        }
};

int main(){
	pint* p = new point(1,3);
    p->print();
}

위 에러가 발생한다면 -std=c++11 compile 옵션으로도 해결가능

#include <iostream>
 
class Point {
    const int conv;
    int& r;
    
public:
    Point(int a, int b) : conv(a), r(b) {
        // conv = a; // If you try assignmet, it'll have an error.
        // r = b;
    }
};
 
int main() {
    Point p(1, 2);
}

 

2. 클래스 안에 디폴트 생성자가 없는 타입이 멤버 데이터로 있을때, 멤버객체변수의 추기

class Point {
    int x;
    int y;
public:
    Point(int a, int b) : x(a), y(b) {}
};
 
class Rect {
    Point p1;
    Point p2;
public:
    Rect(int a, int b, int c, int d) : p1(a, b), p2(c, d) {}
};
 
int main() {
    Rect r; // p1 Point Constructor -> p2 Point Constructor -> Rect Constructor
}

 

 

3. 자식 클래스에서 부모 멤버 변수를 초기화 하여 사용하고 싶을 경우

#include <iostream>
using namespace std;

class point {
	private :
		int x;
		int y;
	public :
		void print(){
			cout << x << " " << y << endl;
		}
};

class circle : public point {
	private :
		int num;
	public :
		circle (int _x, int _y) {
			x = _x;
			y = _y;
		}
};

int main(){
	circle a(1,3);
	a.print();
	cout <<sizeof(a)<<endl;
}

부모의 멤버 변수가 private로 선언되었기 때문에 자식 클래스에서 접근이 불가능하다. 자식 객체가 부모 클래스 멤버를 멤버변수명을 통해 접근은 가능하지만, 부모 클래스의 객체가 다로 생성이 되었기 때문에, access control modifier의 영향을 받는다는 의미.

#include <iostream>

using namespace std;

class point {
	private :
		int x;
		int y;
	public :
		point ( int _x, int _y ){
			x=_x;
			y=_y;
		}
		
		void print(){
			cout << x << " " << y << endl;
		}
};

class circle : public point {
	private :
		int num;
	public :
		circle (int _x, int _y) : x(_x), y(_y) {}
};

int main(){
	circle a(1,3);
	a.print();
	cout <<sizeof(a)<<endl;
}

제어 접근자를 private로 그대로 둔다면, 부모 클래스의 생성자를 통해 멤버를 초기화 해야 하는데, 위 방법으로는 디폴트 생성자만 호출되게 된다.

#include <iostream>

using namespace std;

class point {
	private :
		int x;
		int y;
	public :
		point ( int _x, int _y ){
			x=_x;
			y=_y;
		}
		
		void print(){
			cout << x << " " << y << endl;
		}
};

class circle : public point {
	private :
		int num;
	public :
		circle (int _x, int _y) : point(_x,_y){}
};

int main(){
	circle a(1,3);
	a.print();
	cout <<sizeof(a)<<endl;
}

 

3. 주의 사항

#include <iostream>
 
class Point {
public:
    int x;
    int y;
public:
    Point() : y(0), x(y) {}
};
 
int main() {
    Point p;
    
    std::cout << p.x << std::endl; 
}

Member initializer list will follow the order of member's definition because it's not assignment.

In the above example, when it's using member initializer list, x will be initialized first.
So, X will have garbage value.

 

 

반응형

'Prog&Algol' 카테고리의 다른 글

C++ | Range-based for loops  (0) 2022.03.20
C++ | auto & decltype for type deduction  (0) 2022.03.20
C++ | using namespace  (0) 2022.03.17
2019 Winter Kakao Internship - Hotel room  (0) 2020.04.14
Day-03. DP와 함께 계단 오르기  (0) 2019.12.14

+ Recent posts