类(结构)和对象
1、类的一般形式:
class/struct 类名 :继承方式 父类
{
成员变量;
...
// 构造函数
类名(){}
// 析构函数
~类名(){}
// 类所具有的功能
成员函数
...
public:
//public里面的函数和成员变量是公有的,可以被外部调用。
...
};
一、构造函数
1、构造函数可以重载,可以根据不同的使用场景,对对象进行不同的初始化方式。
无参构造:当没有编写构造函数时,编译器会自动生成一个什么都不做的无参构造函数
有参构造:程序员自己编写的构造函数,一旦编写后,无参构造就不再生成,此时创建
对象时如果不提供参数就会出错,有两种解决方法,一种对有参构造的所有参数设置默
认值,或者手动编写一个空的无参构造。
class String
{
char* c_str;
public:
String(const char* str = NULL)
{
if(NULL == str)
{
c_str = new char[1];
*c_str = '\0';
return;
}
c_str = new char[strlen(str)+1];
strcpy(c_str,str);
}
}
如果对象会被创建成数组对象,这种一般不会初始化,因此一定要提供无参构造。
注意:所谓的“编译器生成的XX函数”,并非语意上的函数,而是功能意义上的函数,因
为编译器作来机器指令的生成都没有必要生成C++代码,而是直接生成具有某些功能的
机器指令即可,没有必须借助高级语言意义上的函数完成。
2、单参构造函数与隐式转换
当构造的函数的参数只有一个时,可以以“类 对象 = 数据” 这样的方式初始化对象,类似
隐式类型转换,可能会造成阅读或其他错误,如果想禁用这种隐式转换,可以在构造函数
前添加explicit关键字。
3、特殊的构造函数—拷贝构造
用一个已经创建好的对象为另一个对象初始时自动调用的函数。它的功能是把旧对象的内
存内容完全拷贝到新对象
编译器会默认生成一个拷贝构造,如果有特殊输求也可以自己手动编写。
如果对象中有指针成员,只拷贝指针变量的地址,叫浅拷贝,这样两个对象会共用一块内存
但这种可能造成段错误,脏数据,内存重复释放的情况
class String
{
public:
String(const String& str)
{
c_str = new char[strlen(str.c_str)+1];
strcpy(c_str,str.c_str);
}
}
二、初始化列表
是一种成员初始化方式,在构造函数后 函数体之前。
class Student
{
const char* name;
const char sex;
short age;
Student(char* n,char s,short a):成员(形参),成员2(形参)....
};
1、为具有const属性的成员初始化。
2、当形参名与成员名相同时,可以区分。
3、也可以直接对所有成员初始化而不需要再写构造函数体。
4、如果成员是数组,则需要大括号包括初始值。
构造函数声明:成员数组{v1,v2,v3}
扩展初始值设定列表只在 -std=c++0x 或 -std=gnu++0x 下可用
5、成员的赋值顺序与初始化列表的顺序无关,与类在中的声明顺序有关。
三、this指针
面临问题:同一类型的对象拥有各自独立的成员变量实例,但彼此共享一份函数代码,不同的对象之间共享成员函数,
成员函数最重要的就是要匹配所访问的成员属于哪个对象。
C++对每个成员函数隐匿式的附加一个参数(对象指针),通过对象调用成员函数时,会自动计算出对象的地址然 后
传递给成员函数
在成员函数中访问类的成员、构造、析构等都是通过this完成,可以显示使用(如果参数名与成员相同时可以
通过this加以区分),也可以隐式使用。
可以把this指针当做参数或返回值达到对象交换的目的。
四、const对象const函数
当创建对象使用const修饰时此对象就不应该再被修改,当通过const对象调用成员函数时,会把对象的地址传递
给成员函数,但普通的成员函数不会附加const this ,因此为的安全,编译器就会报错。
如果一个成员函数确定不会修改const对象的成员,在函数声明后函数体前加const,此函数的this指针就有
const属性,就可以被const对象调用。
const对象只能调用const函数,普通对象调用const函数时也不能修改对象的成员。
如果确实需要在某个const函数中修改成员值有两种方法:
1、const函数与普通可以构成重载,可以再写一个非const版本的函数
2、如果一个成员被 mutable 修饰,那个它可以在const函数中被修改。
五、赋值构造
当一个已有对象给另一个已有对象赋值值就会调用赋值构造。
Student stu1,stu2;
stu1 = stu2; 赋值构造
1、什么情况下会调用赋值构造、拷贝构造
一个对象作为函数的实参传递给函数时 拷贝构造
一个对象作为函数的返回值传递给调用者时 赋值构造 拷贝构造
一个对象给另一个对象初始化时 拷贝构造
2、赋值构造与拷贝构造默认情况编译器都会自动生成,当对象有指针成员需要深拷贝时
就需要手写赋值构造与拷贝构造。
class Test
{
char* str;
public:
String& operator= (const String& str)
{
if(this != &str)
{
String temp(str);
swap(c_str,temp.c_str);
}
return *this;
}
};
六、析构函数(构造函数和析构函数 不能设置为私有)
1、析构函数与构造函数一样都有特殊成员函数,函数名就是类名前加~。
2、析构函数没有参数、没有返回值、不能重载。
3、析构函数在对象销毁时会被自动调用。(先调用,后销毁)
4、一般析构函数负责整个对象的善后工作,可以在对象结束前执行最后一个动作。
5、编译器会自动分配一个析构函数,但如果有分配动态资源、善后工作要作,
就必须自己编写析构函数了,默认的析构函数只负责释放看的到的资源。
6、对象的销毁过程
a)开始销毁对象
b)执行析构函数体
c)逆序调用类成员的析构函数,销毁所有的成员。
d)逆序调用父类的析构函数,销毁所有的父类对象。