
本文共 5931 字,大约阅读时间需要 19 分钟。
目录:(直接使用ctrl+F搜索标题)
1.C++中类的定义 2.class与struct的区别 3.对象的创建 4.初始化表达式 5.对象的销毁 5.1析构函数在哪些时候会被调用呢? 6.拷贝构造函数 6.1拷贝构造函数的调用时机 7.隐含的this指针 8.赋值运算符函数 9.特殊数据成员的初始化 9.1常量数据成员 9.2引用数据成员 9.3类对象成员 9.4静态数据成员 10.特殊的成员函数 10.1静态成员函数 10.2const成员函数 11.对象的组织 11.1const对象 11.2指向对象的指针 11.3对象数组 11.3.1对象数组的声明: 11.3.2对象数组的初始化:可以在声明时进行初始化。 11.4堆对象 12.单例模式正式开始:
1.C++中类的定义
1)概念:C++用类来描述对象,类是对现实世界中相似事物的抽象,比如同是“双轮车”的摩托车和自行车,有共同点,也有许多不同点。“车”类是对摩托车、自行车、汽车等相同点的提取与抽象
2)基本形式:
class Computer{ public: //成员函数 void setBrand(const char * brand) { strcpy(_brand, brand); } void setPrice(float price) { _price = price; } private: //数据成员 char _brand[20]; float _price;};int main(int argc, char * argv[]){ Computer pc;//定义了一个Computer类的对象,这个对象叫pc pc.setBrand("Huawei Matebook14"); pc.setPrice(5600); return 0;}
3)还可以在类外面增加成员函数
类似格式如下:

2.class与struct的区别
1)在C++中,与C相比,struct的功能已经进行了扩展。class能做的事儿,struct一样能做,他们之间唯一的区别,就是默认访问权限不同。class的默认访问权限是private,struct的默认访问权限是public
2)下面是c语言中的结构体
struct Computer{ //成员函数, 其访问权限是public void setBrand(const char *brand) { strcpy(_brand, brand); } void setPrice(float price) { _price = price; } //数据成员, 其访问权限是public char _brand[20]; float _price;};
3)下面是C++中的类
class Computer2{ //成员函数, 其访问权限是private void setBrand(const char *brand) { strcpy(_brand, brand); } void setPrice(float price) { _price = price; } //数据成员, 其访问权限是private char _brand[20]; float _price;};
3.对象的创建
1)在之前的Computer类中,通过自定义的公共成员函数setBrand和setPrice实现了对数据成员的初始化。实际上,C++为类提供了一种特殊的成员函数----构造函数来完成相同的工作。构造函数有一些独特的地方:
a)函数的名字与类名相同 b)没有返回值 c)没有返回类型,即使是void也不能有 2)构造函数在对象创建时自动调用,用以完成对象成员变量等的初始化及其他操作(如为指针成员动态申请内存等);如果程序员没有显式定义它,系统会提供一个默认构造函数。下面我们用一个点Point来举例:class Point{ public: //即使不写,编译器也会自动提供一个 Point()//构造函数:完成对象成员变量等的初始化及其他操作(如为指针成员动态申请内存等) { cout << "Point()" << endl; _ix = 0; _iy = 0; } void print() { cout << "(" << _ix << "," << _iy << ")" << endl; } private: int _ix; int _iy;};int main(void){ Point pt; pt.print(); return 0;}
3)编译器自动生成的缺省(默认)构造函数是无参的,实际上,构造函数可以接收参数,在对象创建时提供更大的自由度。我们在上面的Point类中可以加入一个新的构造函数\
class Point{ public://... Point(int ix, int iy)//构造函数可以接收参数 { cout << "Point(int,int)" << endl; _ix = ix; _iy = iy; }//.... };int main(void){ Point pt(1, 2); pt.print(); Point pt2(11, 12); pt2.print(); return 0;}
4)上面的例子同时出现了无参构造函数,和有参构造函数,这说明了构造函数是可以重载的。在上面,我们说编译器会自动给Point类生成一个默认构造函数,这是有前提条件的,就是类中没有定义任何构造函数。现在假设Point类中只显式定义了一个有参构造函数,则编译器不会再自动提供默认构造函数,如果还希望通过默认构造函数创建对象,则需要显式定义一个默认构造函数
4.初始化表达式
1)在上面的例子中,构造函数对数据成员进行初始化时,都是在函数体内进行的。除此以外,还可以通过初始化列表完成。初始化列表位于构造函数形参列表之后,函数体之前,用冒号开始,如果有多个数据成员,再用逗号分隔,初始值放在一对小括号中。例子如下:
class Point{ public://... Point(int ix = 0, int iy = 0) : _ix(ix) , _iy(iy) { cout << "Point(int = 0,int = 0)" << endl; }//...};
2)如果没有在构造函数的初始化列表中显式地初始化成员,则该成员将在构造函数体之前执行默认初始化。如在“对象的创建”部分的两个构造函数中的_ix和_iy都是先执行默认初始化后,再在函数体中执行赋值操作。可能有同学会觉得在初始化列表中进行成员初始化不习惯,但有些时候成员必须在初始化列表中进行,否则会出现编译报错
3)注意:每个成员在初始化列表之中只能出现一次,其初始化的顺序不是由成员变量在初始化列表中的顺序决定的,而是由成员变量在类中被声明时的顺序决定的。(举例说明)class Foo{ public: Foo(int a) : _iy(a) //在初始化列表中, _iy好像先被初始化 , _ix(_iy) { cout << "Foo(int)" << endl; }private: int _ix; //在声明时,_ix在前 int _iy;};
5.对象的销毁
1)构造函数在创建对象时被系统自动调用,而析构函数(Destructor)在对象被撤销时被自动调用,相比构造函数,析构函数要简单的多。析构函数有如下特点:
a)与类同名,之前冠以波浪号,以区别于构造函数。 b)析构函数没有返回类型,也不能指定参数。因此,析构函数只能有一个,不能被重载。 c)对象超出其作用域被销毁时,析构函数会被自动调用。 2)析构函数在对象撤销时自动调用,用以执行一些清理任务,如释放成员函数中动态申请的内存等。如果 程序员没有显式的定义它,系统也会提供一个默认的析构函数。例如:()class Point{ public://... ~Point() { }//...};
3)由于Point类比较简单,数据成员中没有需要进行清理的资源,所以即使不显式定义析构函数,也没关
系。我们再举一个例子class Computer{ public: Computer(const char *brand, double price) : _brand(new char[strlen(brand) + 1]()) , _price(price) { cout << "Computer(const char *, double)" << endl; } ~Computer() { cout << "~Computer()" << endl; delete [] _brand; _brand = nullptr; } private: char *_brand; double _price;};
以上的Computer中,有一个数据成员是指针,而该指针在构造函数中初始化时已经申请了堆空间的资源,则当对象被销毁时,必须回收其资源。此时,编译器提供的默认析构函数是没有做回收操作的,因此就不再满足我们的需求,我们必须显式定义一个析构函数,在函数体内回收资源。
4)析构函数除了在对象被销毁时自动调用外,还可以显式手动调用,但一般不建议这样使用。 5.1析构函数在哪些时候会被调用呢? 5.1.1. 对于全局定义的对象,每当程序开始运行,在主函数main接受程序控制权之前,就调用构造函数创建全局对象,整个程序结束时,自动调用全局对象的析构函数。 5.1.2. 对于局部定义的对象,每当程序流程到达该对象的定义处就调用构造函数,在程序离开局部对象的作用域时调用对象的析构函数。 5.1.3 对于关键字static定义的静态局部变量,当程序流程第一次到达该对象定义处调用构造函数,在整个程序结束时调用析构函数 5.1.4 对于用new运算符创建的对象,每当创建该对象时调用构造函数,当用delete删除该对象时,调用析构函数6.拷贝构造函数
1)C++中经常会使用一个变量初始化另一个变量,如



Point::Point(const Point &rhs) : _ix(rhs._ix) , _iy(rhs._iy){ }
4)由于Point的成员比较简单,缺省的拷贝构造函数已经可以满足需求了,所以可以不显式定义。接下来,我们把目光转向Compute类,如果Computer类使用缺省拷贝构造函数,会发生什么问题呢?我们先来看看缺省的拷贝构造函数的实现
Computer::Computer(const Computer &rhs) : _brand(rhs._brand) , _price(rhs._price){ }//执行构造初始化Computer pc1("Huawei Matebook14", 5699);//Computer类使用缺省拷贝构造函数Computer pc2 = pc1;
从上面的定义来看,pc与pc对象的数据成员_brand都会指向同一个堆空间的字符串,这种只拷贝指针地址的方式,我们称为浅拷贝。当其中一个对象被销毁时,另外一个对象就获取不到相应的brand值了。此时需要重新显式定义拷贝构造函数,让两个指针不指向同一块堆空间:
Computer::Computer(const Computer & rhs): _brand(new char[strlen(rhs._brand) + 1]()), _price(rhs._price){ strcpy(_brand, rhs._brand);}
这种拷贝指针所指空间内容的方式,我们称为深拷贝。因为两个对象都拥有各自的独立堆空间字符串,一个对象销毁时就不会影响另一个对象
6.1拷贝构造函数的调用时机 6.1.1. 当用一个已经存在的对象初始化另一个新对象时,会调用拷贝构造函数。 6.1.2. 当实参和形参都是对象,进行实参与形参的结合时,会调用拷贝构造函数。 6.1.3. 当函数的返回值是对象,函数调用完成返回时,会调用拷贝构造函数。(优化选项-fno-elide-constructors) 注意:拷贝构造函数的参数形式可以改变吗?(也就是引用符号可以去掉吗?const关键字可以去掉吗?)7.隐含的this指针
8.赋值运算符函数
9.特殊数据成员的初始化
9.1常量数据成员 9.2引用数据成员 9.3类对象成员 9.4静态数据成员 10.特殊的成员函数 10.1静态成员函数 10.2const成员函数 11.对象的组织 11.1const对象 11.2指向对象的指针 11.3对象数组 11.3.1对象数组的声明: 11.3.2对象数组的初始化:可以在声明时进行初始化。 11.4堆对象 12.单例模式发表评论
最新留言
关于作者
