做网站赚广告,做网站应达到什么效果,制作海报,建站之星网站目录 一、初始化列表
1、定义
2、注意事项
3、尽量使用初始化列表初始化
4、初始化顺序
二、 explicit关键字
1、定义
2、特点
三、static成员
1、定义
2、特性
3、例题 一、初始化列表 下面这段代码可以正常编译#xff1a; class A {
private:int _a1;//成员…目录 一、初始化列表
1、定义
2、注意事项
3、尽量使用初始化列表初始化
4、初始化顺序
二、 explicit关键字
1、定义
2、特点
三、static成员
1、定义
2、特性
3、例题 一、初始化列表 下面这段代码可以正常编译 class A {
private:int _a1;//成员声明int _a2;
};
int main()
{A a;//对象整体定义return 0;
} 如果加上一个const类型的成员变量_x编译就无法通过。 class A {
private:int _a1;int _a2;const int _x;
};
int main()
{A a;return 0;
} 这是因为const变量必须在定义的位置初始化否则编译不通过。
class A {
private:int _a1;//声明int _a2;const int _x;
}; 在private作用域中const变量和两个int变量都是成员变量的声明如果我们声明const变量一定要对它进行定义那我们在哪定义呢 C11之后可以在声明位置为变量赋初值。
const int _x 0;
那在C11之前也有解决方法给每个成员变量找一个位置对其进行定义这样就解决了变量初始化的问题这个位置使用初始化列表进行初始化赋值。
1、定义 初始化列表以一个冒号开始接着是一个以逗号分隔的数据成员列表每个成员变量后面跟一个放在括号中的初始值或表达式。 这样就解决了const成员变量初始化问题。 class A {
public:A():_x(1){}
private:int _a1;int _a2;const int _x;
};
int main()
{A a;return 0;
} 只要对象调用构造函数初始化列表是它所有成员变量定义的位置。 不管是否显示在初始化列表写那么编译器每个变量都会初始化列表定义初始化。 class A {
public:A():_x(1),_a1(6){}
private:int _a1 1;int _a2 2;const int _x;
};
int main()
{A a;return 0;
}
在初始化列表中初始化的变量不使用缺省值没有使用初始化列表的变量使用缺省值。
2、注意事项
每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次) 类中包含以下成员必须放在初始化列表位置进行初始化
引用成员变量 const成员变量 自定义类型成员(且该类没有默认构造函数时)
class B {
public:B():_b(0){cout B() endl;}
private:int _b;
};class A {
private:B _bb;
};
int main()
{A aa;return 0;
} 这里的aa的成员变量自定义类型_bb是可以调用它的默认构造函数的初始化列表进行初始化。 默认构造可以是无参或全缺省的。
class B {
public:B(int n) :_b(0)//会报错B(int n9) :_b(0)//全缺省B( ) :_b(0)//无参
private:int _b;
};
3、尽量使用初始化列表初始化 因为不管你是否使用初始化列表对于自定义类型成员变量 一定会先使用初始化列表初始化。 下面看一个用两个栈实现的队列。 typedef int DataType;
class Stack
{
public:Stack(size_t capacity 10){cout Stack(size_t capacity 10) endl;_array (DataType*)malloc(capacity * sizeof(DataType));if (nullptr _array){perror(malloc申请空间失败);exit(-1);}_size 0;_capacity capacity;}void Push(const DataType data){_array[_size] data;_size;}Stack(const Stack st){cout Stack(const Stack st) endl;_array (DataType*)malloc(sizeof(DataType)*st._capacity);if (nullptr _array){perror(malloc申请空间失败);exit(-1);}memcpy(_array, st._array, sizeof(DataType)*st._size);_size st._size;_capacity st._capacity;}~Stack(){cout ~Stack() endl;if (_array){free(_array);_array nullptr;_capacity 0;_size 0;}}private:DataType *_array;size_t _size;size_t _capacity;
};class MyQueue
{
public:MyQueue(int pushN, int popN):_pushST(pushN), _popST(popN){}private:Stack _pushST;Stack _popST;int _size 0;
};int main()
{ MyQueue q(2, 3);return 0;
}
在调试中可以看到这里的2和3分别作为参数传递给MyQueue的构造函数通过初始化列表对这两个成员变量进行初始化。 如果我们使用这种无参的构造函数对MyQueue对象初始化呢 class MyQueue
{
public:MyQueue(){}private:Stack _pushST;Stack _popST;int _size 0;
};
可以看到如果我们不写初始化列表MyQueue类也可以调用Stack的默认构造函数对两个Stack类的对象进行初始化不写MyQueue的构造函数也会使用同样方式初始化本质上一样。 4、初始化顺序
成员变量在类中声明次序就是其在初始化列表中的初始化顺序与其在初始化列表中的先后次序无关。
class A{
public:A(int a):_a1(a), _a2(_a1){}void Print() {cout _a1 _a2 endl;}
private:int _a2;int _a1;
};int main() {A aa(1);aa.Print();
}
_a2比_a1先声明所以_a2比_a1先在初始化列表中初始化_a2初始化时_a1还没初始化所以_a2是随机值。 二、 explicit关键字 class A {
public:A(int a):_a1(a){}
private:int _a2;int _a1;
};
int main()
{A aa1(1); //构造函数A aa2 1; //隐式类型转换int i 1; double d i;//隐式类型转换return 0;
}
默认情况下这里的隐式类型转换都会借助额外创建的临时变量实现通过构造创建临时变量然后拷贝构造给变量赋值的过程被优化为直接构造下一篇文章详细讲解优化过程。 在这两种情况下临时变量的创建是为了完成类型转换的过程。这些临时变量在转换完成后会被销毁对于程序的其他部分是不可见的。这种临时变量的创建和销毁是由编译器自动处理的无需手动干预。 A aa2 1; 这里发生了从int到A的隐式类型转换。编译器会自动调用A类的构造函数来创建一个临时的A对象然后将整数值1传递给构造函数作为参数。这个临时的A对象会被复制到aa2中完成隐式类型转换。 double d i; 这里发生了从int到double的隐式类型转换。编译器会创建一个临时的double变量并将整数变量i的值复制到这个临时变量中。然后这个临时的double变量的值会被赋给变量d完成隐式类型转换。 拷贝构造也属于构造也可以使用初始化列表但下面的成员变量会调用拷贝构造吗 class A
{
public:A(int a):_a1(a){cout A(int a) endl;}A(const A aa):_a1(aa._a1){cout A(const A aa) endl;}private:int _a2;int _a1;
};int main()
{A aa1(1); //构造函数A aa2 1; //隐式类型转换return 0;
}输出结果发现没有调用引用类型的拷贝构造。 这是因为C中编译器会对自定义类型的进行优化 将构造拷贝优化的过程优化成一个构造。 那下面的代码中为什么第一个会报错第二个没问题呢 A ref 10;
const A ref 10;这是因为在C中当你声明一个引用比如 A ref并试图将其初始化为一个右值比如一个临时对象或一个字面量编译器通常会报错。这是因为非const引用不能绑定到右值上防止对临时对象的非常量引用因为这可能导致对临时对象的意外修改从而导致不确定的行为。但是当你声明一个常量引用比如 const A ref编译器允许这种绑定因为常量引用可以绑定到右值上。在上述代码中const A ref 10; 这行代码中的 10 是一个整数字面量是一个右值。你尝试将这个右值绑定到引用 ref 上。由于 ref 被声明为 const A它是一个常量引用所以编译器允许这种绑定并调用 A 类的构造函数 A(int a) 来创建一个临时的 A 对象然后将 ref 绑定到这个临时对象上。
1、定义 构造函数不仅可以构造与初始化对象对于单个参数或者除第一个参数无默认值其余均有默认值 的构造函数还具有类型转换的作用。 对于单参构造函数没有使用explicit修饰具有类型转换作用 explicit修饰构造函数禁止类型转换---explicit去掉之后代码可以通过编译。 对于刚刚的代码如果在构造函数前加explicit程序会怎么样呢
class A{
public:explicit A(int a):_a1(a){cout A(int a) endl;}A(const A aa):_a1(aa._a1){cout A(const A aa) endl;}private:int _a2;int _a1;
};
int main()
{A aa1(1); //构造函数A aa2 1; //隐式类型转换const A ref 10;return 0;
} 这两段代码会报错程序禁止类型转换。 2、特点
class A
{
public://explicit A(int a)A(int a):_a1(a){cout A(int a) endl;}//explicit A(int a1, int a2)A(int a1, int a2):_a1(a1), _a2(a2){}private:int _a2;int _a1;
};
int main()
{// 单参数构造函数 C98A aa1(1); //构造函数A aa2 1; //隐式类型转换// 多参数构造函数 C11A aa2(1, 1);//A aa3 22;//C98不支持A aa3 { 2, 2 };//C11支持return 0;
} A aa1(1); 这是直接调用单参数构造函数创建对象的例子。 A aa2 1; 这是一个隐式类型转换的例子。这里整数1被隐式地转换为类A的一个对象。这是因为类A定义了一个接受int类型参数的构造函数因此编译器会自动调用该构造函数来创建一个临时的A对象并将其赋值给aa2。 A aa2(1, 1); 这是直接调用双参数构造函数创建对象的例子。 A aa3 { 2, 2 }; 这是C11引入的列表初始化的例子。这种方式可以用来初始化对象而不需要显式地调用构造函数。 explicit关键字用于阻止编译器进行不希望发生的隐式类型转换。如果你将构造函数前面的注释去掉使得构造函数前面有explicit关键字那么像A aa2 1;这样的隐式类型转换就会被禁止编译器会报错。 例如如果你将单参数构造函数改为explicit A(int a)那么A aa2 1;这行代码就会导致编译错误因为编译器被禁止进行从int到A的隐式类型转换。你必须显式地调用构造函数像A aa2(1);这样。
总的来说explicit关键字可以帮助你控制类型转换防止因为不希望的隐式类型转换而导致的错误。 三、static成员
实现一个类计算程序中创建了多少类对象
int count 0;
class A
{
public:A(int a 0){count;}A(const A aa){count;}
};
void func(A a)
{}
int main()
{A aa1;A aa2(aa1);func(aa1);A aa3 1;cout count endl;return 0;
}
造成了命名冲突的问题因为C的xutility文件里有个函数count与我们定义的全局变量count冲突了。 我们可以不展开std只调用需要用的流输入输出即可。 #include iostream
//using namespace std;
using std::cout;
using std::endl;
成功输出 C为了解决上述问题同时可以将std展开将count作为类的static修饰的成员即可实现。 1、定义
声明为static的类成员称为类的静态成员用static修饰的成员变量称之为静态成员变量用static修饰的成员函数称之为静态成员函数。静态成员变量一定要在类外进行初始化。静态成员不属于某个对象所于所有对象属于整个类。静态成员变量的初始化通常在类外部进行。 class A
{
public:A(int a 0){count;}A(const A aa){count;}int Getcount(){return count;}
private:static int count; // 此处为声明int _a 0;
};int A::count 0; // 定义初始化void func(A a)
{} 当我们想输出时 int main()
{A aa1;A aa2(aa1);func(aa1);A aa3 1;cout A::Getcount() endl;return 0;
} 输出语句会报错 如果想要输出可以使用静态成员函数。 //静态成员函数 没有this指针static int Getcount(){// _a; // 不能直接访问非静态成员return count;} 成功输出 下面语句创建出了多少个类对象 A aa4[10]; 输出结果 2、特性
静态成员为所有类对象所共享不属于某个具体的对象存放在静态区 静态成员变量必须在类外定义定义时不添加static关键字类中只是声明 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问 静态成员函数没有隐藏的this指针不能访问任何非静态成员 静态成员也是类的成员受public、protected、private 访问限定符的限制
3、例题 链接如下 求123...n_牛客题霸_牛客网 (nowcoder.com) 下面这段代码实现了一个类 Sum 和一个类 Solution其中 Sum 类用于计算从1到n的累加和而 Solution 类则使用 Sum 类来计算给定整数n的累加和。 这种设计利用了类的构造函数和静态成员变量的特性实现了累加和的计算和获取。 class Sum{
public:Sum(){_sum_i;_i;}static int Getsum(){return _sum;}
private:static int _sum;static int _i;
};
int Sum::_sum 0;
int Sum::_i 1;
class Solution {
public:int Sum_Solution(int n) {Sum a[n];return Sum::Getsum();}
};
首先让我们逐步解释 Sum 类的实现
Sum 类有两个静态成员变量 _sum 和 _i分别用于保存累加和和当前的计数器值。构造函数 Sum() 是一个无参构造函数每次被调用时它会将当前计数器值 _i 加到累加和 _sum 中并将计数器 _i 自增1。静态成员函数 Getsum() 用于获取累加和 _sum 的值。
接下来我们来看 Solution 类的实现
Solution 类中的成员函数 Sum_Solution(int n) 接受一个整数 n 作为参数并返回从1到n的累加和。在 Sum_Solution 函数中我们创建了一个名为 a 的 Sum 类型的数组数组的大小为 n。由于 Sum 类的构造函数会在创建对象时自动调用因此创建数组 a 的过程中会依次调用 Sum 类的构造函数从而实现了从1到n的累加和的计算。最后我们通过调用 Sum::Getsum() 函数来获取累加和的值并将其作为函数的返回值。