类和动态分配内存相关知识:
1.类的静态成员只有一个副本,属于类而不属于任何对象(跟成员函数一样),不能在类中初始化静态成员变量,因为类声明不分配任何内存,所以只能在类外定义初始化。(即静态成员在类中是声明,需要在类外进行定义初始化,格式为:数据类型+类名::静态成员名 =val;)
2.在类中使用new来获得指针时要注意,对象保存的仅仅是指针本身,而不是指针指向的数据;
3.特殊的成员函数:(即如果不显示声明,c++自动默认生成的函数)1)默认构造函数 2)默认析构函数 3)复制构造函数 4)赋值运算符 5)地址运算符
4.复制构造函数啥时候用 :当新建一个对象并用已有的对象进行初始化时,将会调用复制构造函数,具体来说,即生成对象副本时会调用复制构造函数:1)函数按值传递 2)函数返回对象 3)产生临时变量
5.复制构造函数功能:默认的复制构造函数逐个复制非静态成员,又俗称浅复制,即复制指针的话只复制指针本身的值,而不复制指针指向的值;当数据成员有指针时,就可能会出现问题;
例如:函数按值传递,原对象传递给被调用函数的参数,调用复制构造函数,进行浅复制,当调用过程结束,调用析构函数释放内存就会把指针指向的内存给释放掉,那么原来对象的指针就会失效。
6.复制构造函数解决办法:当数据成员用new来动态分配内存时,需要显示的定义复制构造函数来进行深度复制来避免上述问题,例如
stock (const stock & st)
{
len =st.len;
str=new char[len+1];
strcpy(str,st.str);
num++;
}
这杨。进行深度复制,创造了指针指向数据的一块副本,就不会出现指针失效的问题了;
7.赋值运算符,当将已有的对象赋值给另外一个对象的时候,将会使用c++提供的默认的赋值运算符(如果用户没有显示的定义的话);注意要与上述初始化对象区分开来,初始化对象可能用到赋值运算符,也可能用不到,但一定会用到复制构造函数,例如:
stringbad metoo =knot
这条初始化语句有两种情况,第一种直接调用复制构造函数来初始化metoo;第二种情况先创建一个临时变量,用复制构造函数初始化它,然后调用赋值运算符将它赋给metoo;因为有这种歧义,所以不管什么类型,最好写出这样:stringbad metoo(knot);
8.赋值运算符与复制构造函数一样,默认都是浅复制,同样可能造成数据失效问题;解决方案同样是定义一个显示复制运算符;
stringbad & operator =(const stringbad & st)
{
if(str==st.str)
return *this;
delete [] str; //在指向新的数据之前,将之前指向的内存给释放掉
len=st.len;
str=new char[len+1]
strcpy(str,st.str);
return *this;
}
8.构造函数要与析构函数相匹配,具体来说:构造函数中的new和析构函数中的delete要相适应,要么是new[]与delete【】匹配,要么是new与delete匹配;这样就不会造成错误;
9,有关返回对象的说明:
1)返回const对象的引用:如果函数参数都是const类型的,那么返回参数的引用也一定要是const类型的;
2)返回非const对象的引用:
一般是重载赋值运算符,重载与cout一起使用的<<运算符,前者是为了提高效率,后者是必须,因为cout没有公有的复制构造函数,所有不能返回对象
3)返回对象:如果返回的对象是局部变量,则不能返回引用,因为被调用函数结束后,局部变量会释放,返回的引用会失效,所以只能调用复制构造函数返回对象,这一部分开销是无法避免的;
4)返回const对象 为了防止返回的对象不会被修改或者出现一些莫名的情况,例如:
force1+force2=net;
这个语句是可以通过的,首先force1和force2会返回一个临时变量,然后再将net赋值给临时变量,这是合法的,但是是没有意义的,所以为了避免这种情况,可以返回const对象;
10,如果对象是动态变量,则执行完该函数的代码块儿,析构函数自动调用;
如果对象是静态变量,则程序执行完,将调用对象的析构函数
如果对象是用new生成的,如果不主动delete,则不会调用析构函数
11.new定位符