不能在类声明中初始化静态成员变量,这是因为声明描述了如何分配内存,但并不分配内存。对于静态类成员,可以再类声明之外使用单独的语句来进行初始化,这是因为静态类成员是单独储存的,而不是成员的组成部分。
例如在声明中有 static int x;//stringbad类
在定义中可有 int stringbad::x=0; //注意没有static
如果为多文件 初始化是在方法文件中,而不是在类声明文件中进行,这是因为类声明位于头文件中,程序可能将头文件包括在几个文件中。如果头文件进行初始化,将出现多个初始化副本,从而引发错误。
静态数据成员在类声明中声明,在包括类方法的文件中初始化。初始化时使用作用域运算符来指出静态成员所属的类。但如果静态成员是const整数类型或枚举型,则可以再类声明中初始化。
在构造函数中使用new来分配内存时,必须在相应的析构函数中使用delete来释放内存。
如果使用new []来分配内存,则应使用delete []来释放内存。
1. 默认构造函数
如果没有提供任何构造函数,x将创建默认构造函数。
例如定义一个kindle类,但没有提供任何构造函数,则编译器将提供下述默认构造函数。
kindle::kindle(){ };
也可以手动进行显式定义默认构造函数
kindle::kindle(){
...
}
但只能有一个默认构造函数。
复制构造函数
复制构造函数用于将一个对象复制到心创建的对象中。也就是说,它用于初始化过程中,而不是常规的赋值过程中。类的复制构造函数原型通常如下:
classname(const classname &);
(拷贝构造函数
拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于:
通过使用另一个同类型的对象来初始化新创建的对象。//注意此处特指
复制对象把它作为参数传递给函数。
复制对象,并从函数返回这个对象。
如果在类中没有定义拷贝构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。拷贝构造函数的最常见形式如下
classname (const classname &obj) {
// 构造函数的主体
}
使用默认拷贝构造函数,那么将不会自动复制堆资源(即通过new得到的资源);所以需要使用拷贝函数使得new得到的资源得以复制。
通常的原则是:①对于凡是包含动态分配成员或包含指针成员的类都应该提供拷贝构造函数;②在提供拷贝构造函数的同时,还应该考虑重载"="赋值操作符号。
假设你有一个类A,和一个已有的类对象b时
当你执行A a=b来创建对象a时调用的是拷贝构造函数,
当你执行A a;a=b时调用的是运算符重载。
)
每当程序生成了对象副本时,编译器都将使用复制构造函数。
默认复制构造函数逐个复制非静态成员(成员复制也称为浅复制),复制的是成员的值,静态量不受影响。但对于指针所指向的的字符串等等,复制后得到的是指针而不是字符串,在原指针被释放后则会出现错误,所以需要定义一个显式复制的构造函数以解决问题。
如果类中包含了使用new初始化的指针成员,应当定义一个复制构造函数,以复制指向的数据,而不是指针,这称为深度复制。复制的另一种形式只是复制指针值。浅复制仅浅浅地复制指针信息,而不会深入挖掘以复制指针引用的结构。
当遇到类中存在指针时最好使用重载=来避免出错(例如字符数组的溢出等等)。
比较成员函数
...
静态类成员函数
可以将成员函数声明为静态的(函数声明必须包含关键字static,但如果函数定义是独立的,则其中不能包含关键字static)
首先 不能通过对象调用静态成员函数;实际上,静态成员函数甚至不能使用this指针。如果静态成员函数是在公有部分声明的,则可以使用类名和作用域解析符来调用它。
其次,由于静态成员函数不与特定的对象相关联,因此只能使用静态数据成员。(而不能使用其他成员)
在构造函数中使用new时应注意的事项
1.如果在构造函数中new初始化指针成员,在析构函数中应使用delete;
2.new和delete必须相互兼容 例如new[]和delete[];
3.如果有多个构造函数,则必须以相同的方式使用new,要么都带[],要么都不带。因为只有一个析构函数,所有的构造函数必须和它兼容。然而可以在一个构造函数中使用new初始化指针,而在另一个构造函数中将指针初始化为空(0或者nullptr),这是因为delete(无论带不带[])可以作用于空指针。
有关返回对象的说明
返回指向const对象的引用
使用const引用的常见原因是旨在提高效率,但对于何时可以采用这种方式存在一些限制,如果函数返回(通过调用对象的方法或将对象作为参数)传递给他的对象,可以通过返回引用来提高效率。
返回指向非const对象的引用
两种常见的返回非const对象情形是:重载赋值运算符以及重载与cout一起使用的<<运算符。前者这样做旨在提高效率后者则必须这样做。
返回对象
如果被返回对象是被调用函数中的局部变量,则不应按引用返回它,因为在被调用函数执行完毕时,局部变量将调用其析构函数。因此若返回引用则引用指向的对象将不再存在。
返回const对象
可以预防错误,强制规范。
总之,如果方法或函数要返回局部变量,则应该返回对象,而不是引用,如果要返回一个没有公有复制构造函数的类的对象(如ostream类)他必须返回一个指向这种对象的引用。最后,当可以返回对象和引用皆可时,应首选引用,因为其效率更高。
使用指向对象的指针 ...
通常如果class_name *pclass= new class_name(value);将调用如下构造函数
class_name(type_name);
这里可能还有一些繁琐的转换,如
class_name (const type_name &);
另外如果不存在二义性,则将发生由原型匹配导致的转换(如从int到double)。下面的初始化方式将调用默认构造函数:
class_name *ptr= new class_name;
在下述情况下析构函数将被调用:
1.如果对象是动态变量,则当执行完定义该对象的程序块时,将调用该对象的析构函数。2.如果对象是静态变量(外部,静态,静态外部或来自名称空间),则在程序结束时将调用对象的析构函数。
3.如果对象是new创造的则仅当您显式使用delete对象时,其析构函数才会被调用。
队列模拟
在类声明中声明的结构、类或枚举被称为是被嵌套在类中,其作用域为整个类。这个声明不会创建数据对象,而只是指定了可以再类中使用的类型。如果声明是在类的私有部分进行的,则只能在这个类使用被声明的类型;如果声明是在公有部分进行的,则可以从类的外部通过作用域解析运算符使用被声明的类型。
当private中有const成员时 在创建对象并初始化时怎么初始化
可以使用成员初始化列表
例Queue:Queue(int qs):qsize(qs)//无法通过正常方式进行直接赋值 因为qsize是一个const 这种写法只能由构造函数来使用 当然你也可以用这种方法对其他变量进行赋值。
而对于const则必须使用这种方法。
另外对于被声明为引用的类成员,也必须使用这种语法。这是因为引用和const数据类似,只能在被创建时进行初始化。对于简单数据成员没什么区别。不能将成员初始化列表语法用于构造函数之外的其他类方法。
成员初始化列表使用的括号方式也可用于常规初始化。
int item=16.2; 等价于 int item(16.2);