c++primer类详解
类的基本思想是数据抽象和封装。数据抽象是依赖接口和实现分离的编程技术。1. 定义抽象数据类型1.1 设计Sales_data类成员函数的声明必须在类内部定义可以在内部或外部作为接口的非成员函数如print、read声明定义都在类的外部。定义在类内部的函数都是隐式的inline函数调用一个成员函数时隐式初始化this指针任何自定义名为this的参数或者变量都是非法的const成员函数const成员函数在参数列表后加上const关键字的函数const的作用是修改隐式this指针的类型默认情况下this的类型是指向类型非常量的常量指针。因此不能将this绑定在一个非常量对象上(不能把this绑定到其他对象)所以也不能在常量对象上调用普通成员函数不能用const 对象访问普通成员函数。const成员函数提高了函数灵活性常量对象以及常量对象的引用或指针只能调用常量成员函数。编译器分两步处理类。1.编译成员声明。2.所有成员声明编译完后编译成员函数体。因此成员声明出现在成员函数体后编译器也可以正常编译在类外定义函数体需要在函数名前加上类名::在类名之后剩余的代码位于作用域之内若返回类型也是在类内声明的就需要在函数名和返回类型前都加上类名::。若在类内声明成了const成员函数在外部定义时const关键字也不能省略。若需要返回类本身使用return *this1.2 定义类相关的非成员函数类相关非成员函数属于类的接口但是不属于类本身。通常把函数声明和定义分开。和类声明在同一头文件内。通常情况下拷贝一个类其实是拷贝其成员。若想拷贝执行其他操作查阅拷贝赋值函数12Sale_data s1;Sale_data s2s1;//s2拷贝了s1的成员1.3构造函数构造函数的任务是初始化类对象的数据成员只要类对象被创建一定会执行构造函数构造函数名与类名相同并且没有返回类型其他与普通函数相同。构造函数不能声明成const默认构造函数默认构造函数无需任何实参若没有为类显式定义任何构造函数编译器隐式构造一个合成的默认构造函数。合成的默认构造函数按照如下规则初始化类成员若存在类内初始值用它来初始化成员否则默认初始化成员某些类不能依赖合成的默认构造函数若类包含内置类型或复合类型成员只有当这些值全被赋予了类内初始值时这个类才适合使用合成的默认构造函数。若类a包含一个成员类b若b没有默认构造函数则编译器无法为a构造正确的默认构造函数若定义了其他构造函数则编译器不会构造默认初始函数1234567classA{//定义了一个实参为string的构造函数//此时编译器不会合成默认构造函数A(std::string a){}}A a;//错误没有默认构造函数A a1(std::string(小黑));//只能用string参数参数列表后加上 defualt表示要求编译器生成默认构造函数defualt可以和声明一起出现在类内也可以作为定义出现在类外。若在类内部则默认构造函数时内联的若在类外部默认不是内联的。1234classA{A()defualt;}A a;//正确编译器生成默认构造函数构造函数初始值列表存在编译器不支持类内初始值这样的话默认构造函数不适用因为默认构造函数使用类内初始值初始化类成员这时应该使用构造函数初始值列表。函数初始值列表是参数列表如下所示冒号以及冒号和花括号间的代码::bookNo(s)构造函数不应该轻易覆盖掉类内初始值除非新赋的值与原值不同在构造函数的过程中没有出现在函数初始化列表中的成员将被执行默认初始化1234567classSales_data{Sales_data(conststd::string s,unsigned n,doublep):bookNo(s),units_sold(n),revenue(p*n){}//当编译器不支持类内初始值时可用如下方法定义Sales_data(conststd::string s):bookNo(s),units_sold(0),revenue(0){}}在类外部定义构造函数,要声明是哪个类的构造函数在函数名前加上类名::123Sales_data::Sales_data(std::istream cin){read(cin,*this);}1.4 拷贝、赋值和析构编译器会为类合成拷贝、赋值和销毁操作。编译器生成的版本对对象的每个成员执行拷贝、赋值和销毁操作2 访问控制和封装访问说明符public说明符后的成员在整个程序内可以被访问private说明符后的成员可以被类的成员函数访问一个类可以包含0个或多个访问说明符有效范围到下一个说明符出现为止。class和struct关键字定义类的唯一区别是class在第一个访问说明符出现之前的区域默认是privatestruct在第一个访问说明符出现之前的区域默认是public2.1 友元类可以允许其他类或函数访问他的非公有成员。方法是用关键字friend声明友元。友元的声明只能在类内部友元声明的位置不限最好在类定义开始或结束前集中声明友元。封装的好处确保用户代码不会无意间破坏封装对象的状态被封装的类的具体实现细节可以随时改变友元在类内的声明仅仅指定了访问权限并不是一个通常意义的函数声明若希望类的用户能够调用某个友元函数需要在友元声明之外再专门对函数进行一次声明为了使友元对类用户可见友元声明与类本身防止在同一个头文件中一些编译器强制限定友元函数必须在使用之前在类的外部声明2.2 类的其他特性接下来介绍类型成员、类的成员的类内初始值、可变数据成员、内联成员函数、从成员函数返回*this、如何定义使用类类型、友元类2.2.1 类成员再探类别名类型成员在类中定义的类型名字和其他成员一样存在访问限制可以是public或者private类别名必须先定义后使用回忆类成员变量可以在类成员函数之后定义但是在类函数中使用原因是编译器先编译类成员变量后边一类成员函数类型成员通常出现在类开始的地方12345classScreen{public://等价于 using pos std::string::size_typetypedefstd::string::size_type pos;}令成员作为内联函数定义在类内部的函数是自动inline的定义在类外部的函数若需要声明内联函数要加上inline;inline成员函数也应该和相应的类定义在同一个头文件夹123456inlineScreen Screen::move(pos r,pos c){pos row r*width;cursor row c;return*this;}可变数据成员永远不会是const即使他是const对象的成员12345678classScreen{publicvoidsome_member()const;private:mutablesize_taccess_ctr;//使用mutable声明可变数据成员}voidScreen::some_member()const{access_ctr;//即使在const成员函数中仍然可以修改可变数据成员}类内初始值使用的初始化形式或者花括号括起来的直接初始化形式2.2.2 返回*this的成员函数注意返回类型是否是引用。是否是引用对函数的使用方法影响很大123456789101112inlineScreen Screen::set(charch){content[cursor] ch;return*this;}inlineScreen Screen ::move(pos r,pos col){cursor r * width col ;return*this;}Screen s(3,2,);//move函数返回s本身所以可以接着调用set函数//并且move函数返回的是Screen的引用若返回的不是引用则会返回一个新的Screen对象s.move(3,2).set(!);从const函数返回的是常量引用在const函数中无法修改类成员变量使用const函数进行重载编写函数display打印Screen中的contents因为只是展示不需要修改值所以这应该是一个const函数。但是希望实现在展示后能移动光标s.display().move(2,3)。这要求display返回的值是可以修改的所以这不应该是const函数。基于const重载可以根据Screen对象是否是const来进行重载。建议多使用do_display这类函数完成实际工作使公共代码使用私有函数可以集中修改没有额外开销123456789101112131415161718192021classScreen{public:Screen* display(std::ostream os){do_display(os);return*this;}constScreen* display(std::ostream os)const{do_display(os);return*this;}private:voiddo_display(std::ostream os)const{oscontent;}}intmain(){constScreen cs(3,3,!);Screen s(3,3,.)cs.display();//因为cs是const的调用第二个const函数s.display();//调用第一个非const的函数}2.2.3 类类型每个类定义了唯一的类型即使成员完全相同也是不一样的类。12345678classA{intmember;}classB{intmember;}A a;B b a;//错误不完全类型类似于函数类也可以只声明不定义,这被叫做不完全类型不完全类型是向程序说明这是一个类名不完全类型使用环境很有限只是可以定义指向这种类型的指针或引用声明但不能定义以不完全类型作为参数或返回类型的函数。类在创建前必须被定义类的成员不能有类本身除了后面介绍的static类但是可以是指向自身的引用或指针2.2.4 友元再探一个类制定了其友元类则友元函数可以访问该类的所有成员友元关系不存在传递性每个类自己负责控制自己的友元类或友元函数定义友元函数的顺序有一个screen类有私有成员content有clear函数可以清除content的内容。1.先声明clear函数2.在screen类中将clear函数函数定义为友元函数3.定义clear函数使用screen类定义友元类有类windowwindow有私有成员content友元类 window_mgr需要直接操作content。正常编写window类在window类中声明friend class window_mgr正常编写 window_mgr类可以直接使用window的content注意将类写在头文件中要按照如下格式否则编译会报错重复的类定义1234#ifndef xxx_H#define xxx_H/class定义///#endif一个类想把一组重载函数定义为它的友元需要对这组函数中的每一个进行友元声明。友元声明仅仅表示对友元关系的声明但并不表示友元这个函数本身的声明12345678910structX{friendviod f(){/*友元函数可以定义在类的内部但是我认为这样没有意义*/X(){f();}//错误f还没有被定义voidg();voidh();}voidX::g(){returnf();}//错误f还没有被定义voidf();voidX::h(){returnf();}//正确f的声明已经在定义中了};2.4 类的作用域定义在类外的方法需要在方法名前使用::说明该方法属于哪一个类在说明属于的类后该函数的作用域位于该类内。即返回类型使用的名字位于类的作用域之外。若返回类型也是类的成员需要在返回类型前使用::指明返回类型属于的类12345//pos的类型声明在window类中并且返回类型在类的作用域外因此要使用window::poswindow::pos window::get_pos(){//在window::get_pos后的所有代码作用域在类内所以返回cursor相当于this-cursorreturncursor;}2.4.1 名字查找和类的作用域类的定义分两步处理1.编译成员的声明2.直到类成员全部可见后编译函数体一般来说内层作用域可以重新定义外层作用域名字但在类中若使用了某个外层作用域中的名字并且该名字表示一种类型则类不能在之后重新定义该名字
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2568064.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!