일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
- std::cout
- constructor
- operator overloading
- virtual inheritance
- 더 지니어스 양면포커
- member function pointer
- dynamic_cast
- delete function
- vector capacity
- virtual function
- conversion constructor
- diamond inheritance
- std::endl
- this call
- new&delete
- virtual function table
- std::ostream
- vector size
- C++
- discord bot
- return by reference
- suffix return type
- virtual destructor
- c++ multi chatting room
- placement new
- base from member
- pointer to member data
- c++ basic practice
- increment operator
- std::vector
- Today
- Total
I'm FanJae.
[C++] Explicit Constructor 본문
1. 복사 초기화 과정의 문제점
class Vector
{
public:
Vector(int size){}
};
int main()
{
// C++98
Vector v1(10);
Vector v2 = 10;
// C++11
Vector v3{10};
Vector v4 = {10};
v1 = 20;
}
- 위 예제는 인자가 한 개(int)인 생성자가 있는 타입의 객체로 총 4가지의 객체 생성 방법을 보인다.
- 이 때, '='가 있으면 Copy Initialization(복사 초기화)가 되고 없으면, Direct Initialization(직접 초기화)가 된다.
- 인자가 한 개인 생성자는 변환의 용도로도 사용이 가능하다.
- 이를 변환 생성자(Conversion constructor)라고도 한다.
v1 = 20; // v1 = Vector(20)
- 즉, 이 경우는 벡터 임시 객체를 만들어서 v1에 보내는 것이다.
class Vector
{
public:
Vector(int size){}
};
void foo(Vector v)
{
}
int main()
{
Vector v1(10);
Vector v2 = 10;
Vector v3{10};
Vector v4 = {10}
v1 = 20;
foo(v1);
foo(10); // ?
}
- 위 예제에서, foo(10) 부분을 풀어 보면, 아래와 같이 풀릴 것이다.
Vector v = 10;
- 이와 같이 변환 한다고 생각하면 논리적으로, foo(10)은 아무런 문제가 되지 않는것처럼 보인다
- 다만,이와 같은 행위가 실수일 수 있었음에도 에러가 발생하지 않은 것일 수 있다.
2. Explicit 생성자
- Direct Initialization만 가능하고, Copy Initialization은 사용할 수 없다.
- 암시적 변환의 용도로 사용될 수 없다. 명시적 변환은 사용가능하다.
class Vector
{
public:
explicit Vector(int size){}
};
void foo(Vector v)
{
}
int main()
{
Vector v1(10); // ok
Vector v2 = 10; // error
Vector v3{10}; // ok
Vector v4 = {10} // error
v1 = 20; // error
foo(v1); // ok
foo(10); // error
}
- 복사 초기화는 함수 인자를 전달하는 원리인데 다소 위험한 상황이 발생할 수 있다.
- 따라서, 안전하게 하기 위해 explicit로 선언하여 잘못된 인자가 가는것을 막는것이다.
2-1. STL과 Explicit 생성자
- STL의 경우는 생성자가 Explicit인 것도 있고, 아닌것도 있다.
#include <vector>
#include <string>
void f1(std::string s) {}
void f2(std::vector<int> v){}
int main()
{
std::string s1("hello");
std::string s2 = "hello"; // ok
std::vector<int> v1(10);
std::vector<int> v2 = 10; // error
f1("hello"); // ok. string s = "hello"
f2(10); // error. vector<int> v = 10
std::vector<int> v3 = 10; // error
std::vector<int> v4 = {10}; // ok
}
- f1("hello")는 string s = "hello"를 보내는 행위로 논리적으로 가능하다.
- f2(10); 은 vector<int> v = 10이다.이는 논리적으로 다소 이상해보일 수 있다.
- 실제로 f1은 Compile Error가 아니나 f2는 Compile Error가 발생한다.
※ 즉, std::string은 Explicit가 아니고, std::vector는 생성자를 Explicit로 작성했음을 알 수 있다.
- 문제는 여기서, vector 사용시 ()와 { }의 차이가 있다.
std::vector<int> v3 = 10; // error
std::vector<int> v4 = {10}; // ok
- vector<int> v(10);의 경우 크기가 10개인 vector를 부르는 것이다.
-이때는 explicit vector(std::size_t)를 호출하여, Explicit이기 때문에 에러가 발생한다
- 한편, vector<int> v {10}; 과 같은 경우, 크기가 한 개(초기값 10)인 vector이다.
- 이때는 vector(std::initializer_list)를 호출하는 것으로 Explicit 생성자가 아니다.
이후, Initializer list에 대해 얘기할 떄, 다시 언급하고자 한다. 일단, 이것이 다른 생성자를 부른다는 사실을 알아두자.
3. Explicit의 또다른 특징
class Object
{
public:
Object() {}
Object(int a, int b) {}
};
void foo ( Object obj ) {}
int main()
{
Object o1;
Object o2{};
Object o3 = {};
Object o4(1,2);
Object o5{1,2};
Object o6 = {1,2};
foo({}); // Object obj {}
foo({1,2}); //Object obj = {1,2};
}
- 위 예제에서 foo({}), foo({1,2})와 같이 보내는 것도 문제되지 않는다.
class Object
{
public:
Object() {}
explicit Object(int a, int b) {}
};
void foo( Object obj ) {}
int main()
{
Object o1;
Object o2{};
Object o3 = {};
Object o4(1,2);
Object o5{1,2};
Object o6 = {1,2}; // Error
foo({}); // Object obj = {}
foo({1,2}); // Object obj = {1,2}; Error
}
- 이와 같이 2개의 인자 값을 받는 경우에 대해서 Explicit를 붙이면 o6과 foo({1,2}) 같이 복사 초기화가 일어나는 경우를 허용하지 않는다.
※ 즉, 인자의 개수가 없거나 여러 개인 경우에도 Explicit를 붙이는 것이 가능하다.
4) explicit(bool) // C++20
#include <type_traits>
class Object
{
public:
explicit(false) Object(int a) {}
};
int main()
{
Object o1(10);
Object o2 = 10;
}
- 특정 객체에 대한 Explicit 여부를 결정할 수 있다.
- 이는 주로 클래스 템플릿을 만들때 활용된다.
#include <type_traits>
class Object
{
public:
template<typename T>
explicit( std::is_integral_v<T> ) Object ( T args )
};
int main()
{
Object o1(10);
// Object o2 = 10;
Object o2 = 3.4;
}
- 위와 같이 std::is_integral_v<T>를 이용해서 정수형인지 여부를 컴파일 타입에 판별하고, 이에 따라서 Explicit 여부를 결정하는데 사용할 수 있다.
'C++ > Basic' 카테고리의 다른 글
[c++] const member function, mutable (0) | 2024.08.17 |
---|---|
[C++] Static Member (0) | 2024.08.16 |
[C++] Member initializer list, Default member initializer (0) | 2024.08.14 |
[C++] Vector I (0) | 2024.08.13 |
[C++] Constructor / Destructor (0) | 2024.08.13 |