C++初始化

C++初始化

C++11之前的初始化

T var;
// 构造
T var(val);
T var= val;
// 列表
T var{val};
T var={val};

C++11统一了初始化方法:列表初始化 brace-initialization
消除了之前初始化基本类型、聚合类型和非聚合类型、及数组和标准容器之间的区别

int i{3}; // 基本类型

// 自定义类型
foo f1{};
foo f2{1,2};

// POD类型
struct bar{
  int a;
  double b;
};
bar b{32, 3.1};

// 数组
int arr[3]{1,2,3};
int* arr= new int[]{1,2,3}; // 动态数组分配

// 标准库容器
std::vector<int> v{1,2,3};
std::map<int,std::string> mp{{1,"one"},{2,"two"}};

// 对于存在参数为`std::initializer_list`的容器类型
std::vector<int> v{10, 20}; //  2 elements: 10,20
std::vector<int> w(10, 20); // 10 elements: all 20

列表初始化--模板

template<typename T>
T copy(T const& val){
  return T{val};
}
auto a= copy(std::string{});
auto b= copy(std::vector<int>{});
auto c= copy(std::vector<std::any>{});

a为空字符串的拷贝
b为空向量的拷贝
c类型为std::vector<std::any>,但是size为1,这也是std::any可以是任意类型变量的原因

template<typename T>
std::vector<T> create(){
  return std::vector<T>{10};
}
int main(){
  auto a= create<std::string>();
  auto b= create<int>();
  auto c= create<char>();
  auto d= create<std::vector<int>>();

  std::cout<<a.size()<<std::endl; // 10 调用了 std::vector<std::string>(10);
  std::cout<<b.size()<<std::endl; // 1
  std::cout<<c.size()<<std::endl; // 1
  std::cout<<d.size()<<std::endl; // 10

  return 0;
}

类型推导

std::vector<int> v{1,2,3};
std::vector<int> w{v.begin()+1, v.end()};
std::vector w2{v.begin()+1, v.end()};

w2的类型是std::vector<std::vector<int>::iterator>

std::vector v2{std::vector{1,2}};

按理解v2的类型应该是:std::vector<std::vector<int>>
但是,其实v2类型为std::vector<int>
此时必须显式指定类型

std::vector<std::vector<int>> v2{std::vector{1,2}};

对象初始化

数据成员初始化

class MyClass{
public:
  MyClass(){}
private:
  std::vector<int> v(1,2); // error
};

报错,可以采用初始化列表方式

class MyClass{
public:
  MyClass():v(1,2){}
private:
  std::vector<int> v;
};

或者

class MyClass{
public:
  MyClass(){}
private:
  std::vector<int> v= std::vector<int>(1,2);
};

对象初始化

class MyClass{};
MyClass f();

编译报错,提示remove parentheses to default-initialize a variable
因为C++无法区分是函数声明还是对象创建

可以使用统一初始化方式:列表初始化
但需要注意,列表是以std::initializer_list方式存在,若构造函数中存在std::initializer_list为参数的构造函数,则优先调用

class MyClass{
public:
  MyClass(){} // 1
  MyClass(int x, int y){} // 2
  MyClass(std::initializer_list<bool> z){} // 3
};
int main(){
  MyClass obj2{};      // call 1
  MyClass obj(5, 1.0); // call 2
  MyClass obj{0, 1};   // call 3
  MyClass obj3({});    // call 3

  return 0;
}

MyClass不存在含有initializer_list参数的构造函数,则MyClass obj{5, 1.0};会调用构造函数MyClass(int x, int y){}

explicit

显式声明关键字
若类的构造函数是单形参的,则应该将的构造函数声明为显式的
以防止隐式的类型转换behind-the-scenes type conversions

如下示例

class IntCell{
public:
  IntCell(){}
  IntCell(int tmp){}
};

正常情况下,C++宽松的法则,会允许下面的隐式转换执行成功

IntCell obj;
obj=37;

等价于下面的伪代码

IntCell obj;
IntCell temp=37;
obj= temp;

单参数构造函数定义了一个隐式的类型转换,在转换时会创建一个临时对象,使得赋值(或函数参数)兼容

对于obj=37;语句,由于类型的不匹配,程序员通常是不希望执行成功的
此时应将单参数构造函数声明为显式的,即explicit IntCell(int tmp){},以避免隐式转换

IntCell obj1;     // 默认构造函数
IntCell obj2(12); // 单参数构造函数

IntCell obj3=12;  // 不合法,explicit
IntCell obj4();   // 函数声明

obj4的混乱现象是使用花括号统一初始化语法的一个原因

IntCell obj4;    // 默认构造函数
IntCell obj4{};  // 默认构造函数
IntCell obj2{3}; // 单参数构造函数
posted @ 2024-11-03 17:35  sgqmax  阅读(2)  评论(0编辑  收藏  举报