1 메모리 할당. 

    int* pArray = new int[4]{0,1,2,3,4};

  

2. 클래스 배열 멤버 생성자에서 초기화.

   class MyClass
   {   
      public:
         MyClass() : mArray{0, 1, 2, 3} {}
      private:
          int mArray[4];
};   

   

3. 복사로 초기화 vs 직접 리스트 초기화. 

   1.   Copy list initialization. T obj = {arg1, arg2, ...};

   2.   Direct list initialization. T obj {arg1, arg2, ...};

 

 

'C++ > C++ 11 14 17' 카테고리의 다른 글

shared_from_this(), weak_from_this()  (0) 2019.05.31
C++17, structure binding, if(init; condition) switch(init;condition)  (0) 2018.04.18
C++ 11 ENUM  (0) 2018.04.11
auto keyword  (0) 2018.04.05
Aggregate and POD  (0) 2018.04.04

C++ 17에서 weak_from_this() 항목이 추가되었다.

 

예제) s

class Foo : public enable_shared_from_this<Foo>
{
public:
   shared_ptr<Foo> getPointer() 

   {
       return shared_from_this();
   }

  weak_ptr<Foo> GetWeakPointer()

  {  

        return weak_from_this();  

  }
};
int main()
{
   auto ptr1 = make_shared();
   auto ptr2 = ptr1->getPointer();
}

 

'C++ > C++ 11 14 17' 카테고리의 다른 글

Uniform Intialzation. - 사용예제  (0) 2019.06.25
C++17, structure binding, if(init; condition) switch(init;condition)  (0) 2018.04.18
C++ 11 ENUM  (0) 2018.04.11
auto keyword  (0) 2018.04.05
Aggregate and POD  (0) 2018.04.04

auto

- 선언으로부터 데이터를 자동으로 유추해서, 컴파일러가 알아서 타입을 지정해주는 키워드.


이점

 1. 선언가 동시에 초기화를 해줘야 하기때문에, 초기화를 하지 않아 발생되는 버그를 예방할 수 있다.

 2. 함수의 리턴 타입과 받는 타입이 달라 발생될 수 있는 버그를 예방 할 수 있다.

   ex) vector 등의 size()는 size_t 타입이지만, 보통, int 형으로 반환받을 경우가 많은데, 자료형 바이트 크기가 달라 문제가 발생될 수 있다.

    vector<int> vecData{1,2,3,4,5};

   auto nSize = vecData.size();

   auto nSize2 = int{vecData.size()}; //에러 발생. not allow narrowing conversion.


3.  처리해야 할 자료형에 대해서 신경을 안써도 된다. 

     vector<int>::iterator iter = vecData.begin(); 보단.

     auto iter = vecData.begin() 으로 처리할 수 있어. 반환 타입을 신경쓰지 않아도 된다.


     auto iterEnd = vecData.end();

     for(auto iter = vecData.begin(); iter != iterEnd; iter++)

{

}




주의사항 

 이동할 수 잆는 자료형은 AUTO를 사용할 수 없다.

  atomic<int> m_nData = 20;

auto atomicData = m_nData;


class CTest

{

public:

CTest() = delete;

};


auto classObject = CTest(); 이경우도 에러.



'C++ > C++ 11 14 17' 카테고리의 다른 글

C++17, structure binding, if(init; condition) switch(init;condition)  (0) 2018.04.18
C++ 11 ENUM  (0) 2018.04.11
Aggregate and POD  (0) 2018.04.04
함수포인터, std::function  (0) 2017.04.19
R 레퍼런스 타입.  (0) 2017.01.30

This article is rather long. If you want to know about both aggregates and PODs (Plain Old Data) take time and read it. If you are interested just in aggregates, read only the first part. If you are interested only in PODs then you must first read the definition, implications, and examples of aggregates and then you may jump to PODs but I would still recommend reading the first part in its entirety. The notion of aggregates is essential for defining PODs. If you find any errors (even minor, including grammar, stylistics, formatting, syntax, etc.) please leave a comment, I'll edit.

What are aggregates and why they are special

Formal definition from the C++ standard (C++03 8.5.1 §1):

An aggregate is an array or a class (clause 9) with no user-declared constructors (12.1), no private or protected non-static data members (clause 11), no base classes (clause 10), and no virtual functions (10.3).

So, OK, let's parse this definition. First of all, any array is an aggregate. A class can also be an aggregate if… wait! nothing is said about structs or unions, can't they be aggregates? Yes, they can. In C++, the term class refers to all classes, structs, and unions. So, a class (or struct, or union) is an aggregate if and only if it satisfies the criteria from the above definitions. What do these criteria imply?

  • This does not mean an aggregate class cannot have constructors, in fact it can have a default constructor and/or a copy constructor as long as they are implicitly declared by the compiler, and not explicitly by the user

  • No private or protected non-static data members. You can have as many private and protected member functions (but not constructors) as well as as many private or protected static data members and member functions as you like and not violate the rules for aggregate classes

  • An aggregate class can have a user-declared/user-defined copy-assignment operator and/or destructor

  • An array is an aggregate even if it is an array of non-aggregate class type.

Now let's look at some examples:

class NotAggregate1
{
  virtual void f() {} //remember? no virtual functions
};

class NotAggregate2
{
  int x; //x is private by default and non-static 
};

class NotAggregate3
{
public:
  NotAggregate3(int) {} //oops, user-defined constructor
};

class Aggregate1
{
public:
  NotAggregate1 member1;   //ok, public member
  Aggregate1& operator=(Aggregate1 const & rhs) {/* */} //ok, copy-assignment  
private:
  void f() {} // ok, just a private function
};

You get the idea. Now let's see how aggregates are special. They, unlike non-aggregate classes, can be initialized with curly braces {}. This initialization syntax is commonly known for arrays, and we just learnt that these are aggregates. So, let's start with them.

Type array_name[n] = {a1, a2, …, am};

if(m == n)
the ith element of the array is initialized with ai
else if(m < n)
the first m elements of the array are initialized with a1, a2, …, am and the other n - m elements are, if possible, value-initialized (see below for the explanation of the term)
else if(m > n)
the compiler will issue an error
else (this is the case when n isn't specified at all like int a[] = {1, 2, 3};)
the size of the array (n) is assumed to be equal to m, so int a[] = {1, 2, 3}; is equivalent to int a[3] = {1, 2, 3};

When an object of scalar type (boolintchardouble, pointers, etc.) is value-initialized it means it is initialized with 0 for that type (false for bool0.0 for double, etc.). When an object of class type with a user-declared default constructor is value-initialized its default constructor is called. If the default constructor is implicitly defined then all nonstatic members are recursively value-initialized. This definition is imprecise and a bit incorrect but it should give you the basic idea. A reference cannot be value-initialized. Value-initialization for a non-aggregate class can fail if, for example, the class has no appropriate default constructor.

Examples of array initialization:

class A
{
public:
  A(int) {} //no default constructor
};
class B
{
public:
  B() {} //default constructor available
};
int main()
{
  A a1[3] = {A(2), A(1), A(14)}; //OK n == m
  A a2[3] = {A(2)}; //ERROR A has no default constructor. Unable to value-initialize a2[1] and a2[2]
  B b1[3] = {B()}; //OK b1[1] and b1[2] are value initialized, in this case with the default-ctor
  int Array1[1000] = {0}; //All elements are initialized with 0;
  int Array2[1000] = {1}; //Attention: only the first element is 1, the rest are 0;
  bool Array3[1000] = {}; //the braces can be empty too. All elements initialized with false
  int Array4[1000]; //no initializer. This is different from an empty {} initializer in that
  //the elements in this case are not value-initialized, but have indeterminate values 
  //(unless, of course, Array4 is a global array)
  int array[2] = {1, 2, 3, 4}; //ERROR, too many initializers
}

Now let's see how aggregate classes can be initialized with braces. Pretty much the same way. Instead of the array elements we will initialize the non-static data members in the order of their appearance in the class definition (they are all public by definition). If there are fewer initializers than members, the rest are value-initialized. If it is impossible to value-initialize one of the members which were not explicitly initialized, we get a compile-time error. If there are more initializers than necessary, we get a compile-time error as well.

struct X
{
  int i1;
  int i2;
};
struct Y
{
  char c;
  X x;
  int i[2];
  float f; 
protected:
  static double d;
private:
  void g(){}      
}; 

Y y = {'a', {10, 20}, {20, 30}};

In the above example y.c is initialized with 'a'y.x.i1 with 10y.x.i2 with 20y.i[0] with 20y.i[1] with 30 and y.f is value-initialized, that is, initialized with 0.0. The protected static member d is not initialized at all, because it is static.

Aggregate unions are different in that you may initialize only their first member with braces. I think that if you are advanced enough in C++ to even consider using unions (their use may be very dangerous and must be thought of carefully), you could look up the rules for unions in the standard yourself :).

Now that we know what's special about aggregates, let's try to understand the restrictions on classes; that is, why they are there. We should understand that memberwise initialization with braces implies that the class is nothing more than the sum of its members. If a user-defined constructor is present, it means that the user needs to do some extra work to initialize the members therefore brace initialization would be incorrect. If virtual functions are present, it means that the objects of this class have (on most implementations) a pointer to the so-called vtable of the class, which is set in the constructor, so brace-initialization would be insufficient. You could figure out the rest of the restrictions in a similar manner as an exercise :).

So enough about the aggregates. Now we can define a stricter set of types, to wit, PODs

What are PODs and why they are special

Formal definition from the C++ standard (C++03 9 §4):

A POD-struct is an aggregate class that has no non-static data members of type non-POD-struct, non-POD-union (or array of such types) or reference, and has no user-defined copy assignment operator and no user-defined destructor. Similarly, a POD-union is an aggregate union that has no non-static data members of type non-POD-struct, non-POD-union (or array of such types) or reference, and has no user-defined copy assignment operator and no user-defined destructor. A POD class is a class that is either a POD-struct or a POD-union.

Wow, this one's tougher to parse, isn't it? :) Let's leave unions out (on the same grounds as above) and rephrase in a bit clearer way:

An aggregate class is called a POD if it has no user-defined copy-assignment operator and destructor and none of its nonstatic members is a non-POD class, array of non-POD, or a reference.

What does this definition imply? (Did I mention POD stands for Plain Old Data?)

  • All POD classes are aggregates, or, to put it the other way around, if a class is not an aggregate then it is sure not a POD
  • Classes, just like structs, can be PODs even though the standard term is POD-struct for both cases
  • Just like in the case of aggregates, it doesn't matter what static members the class has

Examples:

struct POD
{
  int x;
  char y;
  void f() {} //no harm if there's a function
  static std::vector<char> v; //static members do not matter
};

struct AggregateButNotPOD1
{
  int x;
  ~AggregateButNotPOD1() {} //user-defined destructor
};

struct AggregateButNotPOD2
{
  AggregateButNotPOD1 arrOfNonPod[3]; //array of non-POD class
};

POD-classes, POD-unions, scalar types, and arrays of such types are collectively called POD-types.
PODs are special in many ways. I'll provide just some examples.

  • POD-classes are the closest to C structs. Unlike them, PODs can have member functions and arbitrary static members, but neither of these two change the memory layout of the object. So if you want to write a more or less portable dynamic library that can be used from C and even .NET, you should try to make all your exported functions take and return only parameters of POD-types.

  • The lifetime of objects of non-POD class type begins when the constructor has finished and ends when the destructor has finished. For POD classes, the lifetime begins when storage for the object is occupied and finishes when that storage is released or reused.

  • For objects of POD types it is guaranteed by the standard that when you memcpy the contents of your object into an array of char or unsigned char, and then memcpy the contents back into your object, the object will hold its original value. Do note that there is no such guarantee for objects of non-POD types. Also, you can safely copy POD objects with memcpy. The following example assumes T is a POD-type:

    #define N sizeof(T)
    char buf[N];
    T obj; // obj initialized to its original value
    memcpy(buf, &obj, N); // between these two calls to memcpy,
    // obj might be modified
    memcpy(&obj, buf, N); // at this point, each subobject of obj of scalar type
    // holds its original value
  • goto statement. As you may know, it is illegal (the compiler should issue an error) to make a jump via goto from a point where some variable was not yet in scope to a point where it is already in scope. This restriction applies only if the variable is of non-POD type. In the following example f() is ill-formed whereas g() is well-formed. Note that Microsoft's compiler is too liberal with this rule—it just issues a warning in both cases.

    int f()
    {
      struct NonPOD {NonPOD() {}};
      goto label;
      NonPOD x;
    label:
      return 0;
    }
    
    int g()
    {
      struct POD {int i; char c;};
      goto label;
      POD x;
    label:
      return 0;
    }
  • It is guaranteed that there will be no padding in the beginning of a POD object. In other words, if a POD-class A's first member is of type T, you can safely reinterpret_cast from A* to T*and get the pointer to the first member and vice versa.

The list goes on and on…

Conclusion

It is important to understand what exactly a POD is because many language features, as you see, behave differently for them.

'C++ > C++ 11 14 17' 카테고리의 다른 글

C++ 11 ENUM  (0) 2018.04.11
auto keyword  (0) 2018.04.05
함수포인터, std::function  (0) 2017.04.19
R 레퍼런스 타입.  (0) 2017.01.30
Modern C++ [MS]  (0) 2016.12.28

함수 포인터 개념부터 std::function까지

 

개념 및 기초

  1. 함수의 시작 번지를 가르키는 포인터 (어셈블리 언어의 호출 스택을 생각해보자)
  2. 포인터 형식 자체가 다르다.
  • 리턴타입(*함수 포인터 이름)(인수목록)
  • 함수 : int Func(int a) => 포인터 : int(*funcPtr)(int)
  • 함수의 시작번지에 들어가기 앞서 인수들의 목록과 리턴 형식(주소) 정보를 (스택에) 저장해야하므로

함수 포인터 사용 방법

  1. 대입 & 호출
  • 대입 : funcPtr = Func; (괄호없이 단독으로 사용된 함수명은 함수의 시작 번지를 나타내는 포인터 상수.)
  • 호출 : 원칙적으로 (*funcPtr)(2); 하지만 컴파일러는 funcPtr(2); 도 동작하게한다.
  • 인자가 잘못 들어가거나 리턴형식을 잘못된 타입으로 받으면 당연히 컴파일 에러, 함수 포인터 형식이 복잡한 이유
  1. 하나의 고유한 타입인 함수 포인터.
  • 원형이 다른 함수 포인터끼리 직접 대입, 복사 불가.
int(*funcPtr1)(int), double(*funcPtr2)(float);
funcPtr1 = funcPtr2; //에러
  • 캐스팅 가능
int(*funcPtr1)(int), double(*funcPtr2)(float);
funcPtr1 = (int(*)(int))funcPtr2;
  1. 응용
  • 함수 포인터 배열
int(*funcPtrArr[MAX_FUNC_NUM])(int); //함수 포인터 변수명 다음에 배열 크기 선언
funcPtrArr[0] = funcPtr1;
  • 함수 포인터의 포인터
int(**funcPPtr)(int); //일단 이건데 불편, 위에도 마찬가지
typedef int(*FUNCPTR)(int); //함수 포인터 타입을 typedef로 정의하고(정의 형식 주의)
FUNCPTR* funcPPtr; //이렇게나
FUNCPTR funcPtr[MAX_FUNC_NUM]; //이렇게 사용하는 것이 가독성이 높다.

함수 포인터의 의미

  1. 함수를 변수로 사용할 수 있다.
  • 하나의 함수포인터만을 가지고 조건에 따라 다른 함수를 실행시킬 수 있다. (fsm에서 활용하는 방법)
  • 함수를 인자로 넘길 수 있다.
  • 함수 포인터 배열이나 구조체를 사용하여 전체 함수그룹을 변경하는 것도 가능하다.
  1. 사용하기 좋은 경우
  • 조건에 따라 선택해야 할 함수가 두 개 이상인 경우. 함수 포인터 배열을 활용하자
  • 함수 선택 분기와 실제 호출 시점이 다른 경우. 미리 선택한뒤 변수에 저장된 함수를 바로 호출하면 된다.
  • 호출할 함수가 외부 모듈(DLL)에 있는 경우, 동적 연결하려면 함수 포인터 사용해야한다.

함수 인자 사용하기

  1. 예제 : 퀵소트
  • 퀵소트 형식
  void qsort(void *base, size_t num, size_t width,
  int ( *compare )(const void *, const void *));
  • compare 함수
 int compare(const void *a, const void *b)
 {
      if (*(int *)a == *(int *)b) return 0;
      if (*(int *)a > *(int *)b) return 1;
      return -1;
 }
  • 실제 퀵소트 호출
int ar[]={34,25,27,19,4,127,9,629,18,7,9,165};
qsort(ar,sizeof(ar)/sizeof(ar[0]),sizeof(int),compare);
for (int i=0;i<sizeof(ar)/sizeof(ar[0]);i++) {
     printf("%d번째 = %d\n",i,ar[i]);
}

클래스 멤버함수의 함수 포인터

  1. 클래스 내부에 있는 메소드, 멤버함수들을 함수 포인터로 사용하고 싶을때.
  2. 기존 C의 함수들은 정적(Static)함수로, 소속이나 사용 인스턴스와 무관하게 사용되었다.
  3. 하지만 클래스의 멤버함수들은 Class 이름을 네임스페이스로 (Class::Function();)
    인스턴스에 의존적으로(this->Function();) 사용되므로 차이가 있다.
  4. 예전 스터디 static부분참조
  5. 실제 코드로 보는 일반/멤버함수 포인터의 차이점과 사용방법 예시
class World
{
 public:
   void Close() {}
   static void Open() {}
};

void foo(){}
int main()
{
   //대입
   void(*fPtr1)() = &foo;            //일반 함수 포인터
   void(*fPtr2)() = &World::Open;    //클래스 멤버 함수 포인터. 
   //클래스의 네임스페이스 설정이 안되어 있으나 static이므로 접근 가능
   void(*fPtr3)() = &World::Close;   //함수 포인터 소속 불명.
   void(World::*fPtr4)() = &World::Close; //함수의 소속을 명시하면 OK

   //호출
   fPtr4();                          //하지만 인스턴스에 바인딩 되지 않으면 에러
   World earth; 
   earth.fPtr4();                   //earth 내부의 멤버함수 fptr4를 찾기때문에 불가능
   (earth.*fPtr4)();                //원칙적 사용방법 이렇게 쓰도록 한다.
}
  1. 결론
  • 멤버함수를 클래스 외부에서 함수 포인터로 선언(사용)할 때 타입에 클래스 소속을 명시해서 써야한다.
  • 멤버함수 호출할때 인스턴스를 참조해서 호출해야한다.

std::function

  1. 일반 함수 포인터의 문제
  • 반환값이 명시적으로 (암시적 형변환 불가) 같은 타입이 아니면 컴파일 에러
  • 오로지 함수만 호환이 가능하다. (functor, 멤버함수 포인터, 람다함수, bind반환값 등 안됨)
  1. std::function을 사용하는 것의 장점
  • 함수 반환값이 암시적 형변환 가능한 타입이면 대입 가능하다.
  • static 함수 포인터 말고도 functor, 멤버함수 포인터, 람다함수, bind반환값 등 위아더월
  • 유연한 사용이 가능하다. (특히 인자로 함수를 받는 경우. 다양한 활용가능)

std::bind

  1. 일련의 인자들을 함수에 바인딩시킨 함수 객체를 반환하는 함수 템플릿
  • std::bind(함수 포인터, 인자...);

  • 예시

      void show_text(const string& t)
     {
         cout << "TEXT: " << t << endl;
     }
     // show_text 함수에 "Bound function"이라는 문자열을 바인딩시킨
     // function<void ()> 타입을 반환한다.
     function <void ()> f = std::bind(show_text, "Bound function");
  1. std::placeHolder를 활용하여 추가 인수 bind
  • bind의 인자 대신 std::placeHolders::_1, _2, _3... _N을 입력
  • 예시

int multiply(int a, int b) { return a * b; }

int main() { // function f 는 multiply 함수에 a 인자는 5로 고정시키고, b 인자는 placeholding만 한다. // 이럼으로써, function f는 하나의 인자(x)를 취하지만, // 그것이 결국 multiply(5, x)의 형태로 함수를 호출하는 것이다.

   auto f = bind(multiply, 5, _1);

   for (int i = 0; i < 10; i++)
   {
       cout << "5 * " << i << " = " << f(i) << endl;
   }
   return 0;

} ```

  1. 클래스 멤버함수의 경우 this를 바인딩해서 쉽게 사용가능
  • reference_wrapper를 사용하여 인스턴스를 참조로 넘겨야 복사가 발생하지 않는다고 한다.(잘 모르겠음)

  • 예시

    BindTest bt;
    auto f2 = bind(&BindTest::MemberFunc, std::ref(bt), _1);
    f2(10);  // bt.MemberFunc(10);

함수 객체 (functor)

  1. 객체를 함수처럼 사용하는 것
  • 객체의 식별자를 함수 이름처럼 : ()연산자를 오버로딩
  • algorithm함수에서 많이 사용된다.
  1. 함수 포인터와 차이점
  • 멤버 변수, 멤버 함수를 가질 수 있다.
  • template 클래스로 범용적으로 사용할 수 있다

 

퍼옴: https://github.com/jwvg0425/ProjectArthas/wiki/%ED%95%A8%EC%88%98-%ED%8F%AC%EC%9D%B8%ED%84%B0-%EA%B0%9C%EB%85%90%EB%B6%80%ED%84%B0-std::function%EA%B9%8C%EC%A7%80

'C++ > C++ 11 14 17' 카테고리의 다른 글

C++ 11 ENUM  (0) 2018.04.11
auto keyword  (0) 2018.04.05
Aggregate and POD  (0) 2018.04.04
R 레퍼런스 타입.  (0) 2017.01.30
Modern C++ [MS]  (0) 2016.12.28

1. 깊은 복사를 대신 R-VALUE의 값을 복시하고, R-VALUE는 초기화.

2. 생성자와 R-VALUE 할당연산자를 구성해줘야 한다.


3. R-VALUE 레퍼런스 선언은 && 입니다.

class CA
{
    int* pValue;
public:
    CA()
    {
        pValue = NULL;
    }

    CA(CA& va)
    {
        if (this == &va) return;

        Alloc();
        *pValue = *va.pValue;
    }


    CA(CA&& va) :pValue(va.pValue)
    { 
        va.pValue = NULL;
    }
    CA& operator=(CA&& va)
    {
        pValue = va.pValue;
        va.pValue = NULL;
        return *this;
    }

    ~CA()
    {
        if (pValue)
        {
            delete pValue;
        }
    }


    void Alloc()
    {
        pValue = new int;
        *pValue = 100;
    }

};

CA GetValue()
{
    CA VALUE;
    VALUE.Alloc();
    return VALUE;
}

4. 상수는 호출 할 수 있지만, L-VALUE 인 경우, std::move 를 호출해야 한다. 

   void Test(int&& i)

   {   

   }


   Test(1) -> ok
   int a = 10;
   Test(a) -> failed
   Test(std::move(a)) ->ok

  


'C++ > C++ 11 14 17' 카테고리의 다른 글

C++ 11 ENUM  (0) 2018.04.11
auto keyword  (0) 2018.04.05
Aggregate and POD  (0) 2018.04.04
함수포인터, std::function  (0) 2017.04.19
Modern C++ [MS]  (0) 2016.12.28

바로가기

https://docs.microsoft.com/ko-kr/cpp/cpp/support-for-cpp11-14-17-features-modern-cpp

'C++ > C++ 11 14 17' 카테고리의 다른 글

C++ 11 ENUM  (0) 2018.04.11
auto keyword  (0) 2018.04.05
Aggregate and POD  (0) 2018.04.04
함수포인터, std::function  (0) 2017.04.19
R 레퍼런스 타입.  (0) 2017.01.30

+ Recent posts