C++面向对象(一)——类与对象
2016-04-28 21:30
323 查看
定义类
关键字class开头,后跟类的名称。类的主体被包在花括号中。类定义后必须跟着一个分号或列表。class Line{ private: double length; public: void setLength(double len); double getLength(); };
定义对象
声明类的对象,就像声明基本类型的变量一样。注意区分普通变量和引用变量的区别。Line line1 ;//声明普通变量 Line* line2 = new Line;//声明指针变量 delete line2;
普通变量的内存占用由编译器去管理 。指针变量的内存占用由开发人员手动管理
访问数据成员
普通变量和指针变量访问数据成员的方式不一样,都只能访问公共成员变量和函数——用public 声明。Line line1 ; Line* line2 = new Line; line1.setLength(10.0); line2->setLength(11.1); cout<<"line1 "<<line1.getLength()<<endl; cout<<"line2 "<<line2->getLength()<<endl; delete line2;
普通变量的公共成员用运算符(.)访问。指针变量的公共成员用运算符(->)访问。
类成员函数
类的成员函数是指那些把定义和原型写在类定义内部的函数,就像类定义中的其他变量一样。类成员函数是类的一个成员,它可以操作类的任意对象,可以访问对象中的所有成员。成员函数可以定义在类定义内部,或者单独使用范围解析运算符(::)来定义。在类定义中定义的成员函数把函数声明为内联的,即便没有使用 inline 标识符。
class Line{ private: double length; public: void setLength(double len); double getLength(); void print(){//定义在类内部,也就是内联函数 cout<<"line's length is "<<length<<endl; cout<<"called by print"<<endl; } }; /** *类的外部使用范围解析运算符 :: 定义该函数 */ void Line::setLength(double len){ length = len; } double Line::getLength(){ return length; }
内联函数
内联函数从源代码层看,有函数的结构,而在编译后,却不具备函数的性质。内联函数不是在调用时发生控制转移,而是在编译时将函数体嵌入在每一个调用处。编译时,类似宏替换,使用函数体替换调用处的函数名。一般在代码中用inline修饰,但是能否形成内联函数,需要看编译器对该函数定义的具体处理。//用关键字inline定义内联函数 inline void printLine(Line line){ cout<<"line's length is "<<line.getLength()<<endl; cout<<"called by printLine"<<endl; }
构造函数与析构函数
构造函数是一种特殊的成员函数,会在每次创建类的新对象时执行。析构函数函数则在每次删除所创建的对象时执行。特征 | 构造函数 | 析构函数 |
---|---|---|
名称 | 类名 | ~类名 |
返回类型 | 无 | 无 |
参数 | 可以有参数 | 不可以有参数 |
用途 | 为某些成员变量设置初始值 | 在跳出程序前释放资源 |
其他 | 能够重载 | 不能够重载 |
class Line{ private: double length; public: void setLength(double len); double getLength(); void print(){ cout<<"line's length is "<<length<<endl; cout<<"called by print"<<endl; } Line();//没有参数的构造函数 Line(double len);//带有参数的构造函数 ~Line();//析构函数 }; Line::Line(){ length=0; cout<<"object is created "<<length<<endl; } Line::Line(double len){ length = len; cout<<"object is created "<<length<<endl; } Line::~Line(){ cout<<"object is deleted"<<endl; } int main(int argc, char *argv[]) { Line line1(12) ;//调用有参数的构造函数 Line* line2 = new Line;//调用无参数的构造函数 line1.setLength(10.0); line2->setLength(11.1); cout<<"line1 "<<line1.getLength()<<endl; cout<<"line2 "<<line2->getLength()<<endl; delete line2;//调用析构函数。程序结束时编译器自动释放line1 的资源,再次调用析构函数一次 }
运行结果
object is created 12 object is created 0 line1 10 line2 11.1 object is deleted object is deleted
分析运行结果得知,定义两个变量,构造函数和析构函数各调用两次。其中,指针变量需要手动
delete才能调用析构函数。有兴趣的可以自行验证。
拷贝构造函数
一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于:
通过使用另一个同类型的对象来初始化新创建的对象。
复制对象,把它作为参数传递给函数。
复制对象,并从函数返回这个对象
如果在类中没有定义拷贝构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。定义拷贝构造函数,参照如下形式:
classname (const classname &obj) { // 构造函数的主体 }
给Line类添加上拷贝构造函数
class Line{ private: double length; public: void setLength(double len); double getLength(); void print(){ cout<<"line's length is "<<length<<endl; cout<<"called by print"<<endl; } Line();//没有参数的构造函数 Line(double len);//带有参数的构造函数 ~Line();//析构函数 Line(const Line &obj);//拷贝构造函数 }; Line::Line(const Line &obj){ cout<<"copy Line"<<endl; length = obj.length; } int main(int argc, char *argv[]) { Line line1(12) ;//调用有参数的构造函数 Line* line2 = new Line;//调用无参数的构造函数 line1.setLength(10.0); line2->setLength(11.1); printLine(line1); cout<<"line1 "<<line1.getLength()<<endl; cout<<"line2 "<<line2->getLength()<<endl; delete line2;//调用析构函数。程序结束时编译器自动释放line1 的资源,再次调用析构函数一次 }
object is created 12 object is created 0 copy Line line's length is 10 called by printLine object is deleted line1 10 line2 11.1 object is deleted object is deleted
眼尖的伙伴也许发现了这里调用了三次析构函数。分析第三行到第6行的输出,调用函数
printLine首先会调用拷贝构造函数给形参复初始化,再执行
printLine的函数体,最后释放形参暂用的资源。
友元函数
友元可以是一个函数,该函数被称为友元函数。友元也可以是一个类,该类被成为友元类。在这种情况下,整个类及其所有成员都是友元。友元函数是定义在类的外部却可以访问类的内部成员(private 和protected)。友元函数的原型在类的内部定义,但是不是类的成员函数。通常用关键字 friend 修饰声明的函数为一个类的友元。
class Line{ private: double length; public: void setLength(double len); double getLength(); void print(){ cout<<"line's length is "<<length<<endl; cout<<"called by print"<<endl; } Line();//没有参数的构造函数 Line(double len);//带有参数的构造函数 ~Line();//析构函数 Line(const Line &obj);//拷贝构造函数 friend void print(Line line);//定义为line类的友元函数 }; inline void printLine(Line line){ cout<<"line's length is "<<line.getLength()<<endl; cout<<"called by printLine"<<endl; } //printf不是任何类的成员函数,因此不需要修饰符:: void printf(Line line){ //友元函数可以直接访问该类的任何成员 cout<<"line's length is "<<line.length<<endl; cout<<"called by printLine"<<endl; }
对比友元函数、内联函数、成员函数的定义。成员函数和友元函数可以访问类的内部成员。但是友元函数必须基于类的对象访问类的内部成员。友元函数和内联函数不需要范围解析运算符,因为他们不属于类的成员函数。
this指针
每一个对象都能通过 this 指针来访问自己的地址。this 指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象。友元函数没有 this 指针,因为友元不是类的成员。只有成员函数才有 this 指针。
Line Line::changeLine(double length){ this->length = length;//第一个length是类的成员变量,第二个length是形参 cout<<"line's length is "<<this->length<<endl; cout<<"called by changeLine"<<endl; return *this;//返回自己的地址 } int main(int argc, char *argv[]) { Line line1(12) ;//调用有参数的构造函数 Line* line2 = new Line;//调用无参数的构造函数 line1.setLength(10.0); line2->setLength(11.1); printLine(line1); cout<<"line1 "<<line1.getLength()<<endl; cout<<"line2 "<<line2->getLength()<<endl; Line line3 = line2->changeLine(23); cout<<"line3 "<<line3.getLength()<<endl; delete line2;//调用析构函数。程序结束时编译器自动释放line1,line3的资源,再次调用析构函数两次 }
object is created 12 object is created 0 copy Line line's length is 10 called by printLine object is deleted line1 10 line2 11.1 line's length is 23 called by changeLine copy Line line3 23 object is deleted object is deleted object is deleted
分析第9~13行的输出,
return *this;会再次生成一个line对象。因此后续会依次释放line1、line2、line3 的资源。
静态成员
在类的定义中,用static 关键字把类的成员定义为静态的。无论创建多少个对象,静态成员都只有一个副本。访问静态成员的方式:classname :: staticmember。静态成员可以是成员变量也可以是成员函数。//完整版Line类 #include <iostream> using namespace std; class Line{ private: double length; static int objcount;//声明静态成员变量 public: void setLength(double len); double getLength(); void print(){ cout<<"line's length is "<<length<<endl; cout<<"called by print"<<endl; } Line();//没有参数的构造函数 Line(double len);//带有参数的构造函数 ~Line();//析构函数 Line(const Line &obj);//拷贝构造函数 friend void print(Line line);//定义为line类的友元函数 Line changeLine(double length); static int getCount(); }; int Line::objcount = 0;//定义并初始化静态成员变量 Line::Line(){ length=0; cout<<"object is created "<<length<<endl; objcount++; cout<<objcount<<" objects left"<<endl; } Line::Line(double len){ length = len; cout<<"object is created "<<length<<endl; objcount++; cout<<objcount<<" objects left"<<endl; } Line::~Line(){ cout<<"object is deleted"<<endl; objcount--; cout<<objcount<<" objects left"<<endl; } int Line::getCount(){ return objcount; } Line::Line(const Line &obj){ cout<<"copy Line"<<endl; length = obj.length; objcount++; cout<<objcount<<" objects left"<<endl; } void Line::setLength(double len){ length = len; } double Line::getLength(){ return length; } inline void printLine(Line line){ cout<<"line's length is "<<line.getLength()<<endl; cout<<"called by printLine"<<endl; } //printf不是任何类的成员函数,因此不需要修饰符:: void print(Line line){ //友元函数可以直接访问该类的任何成员 cout<<"line's length is "<<line.length<<endl; cout<<"called by printLine"<<endl; } Line Line::changeLine(double length){ this->length = length; cout<<"line's length is "<<this->length<<endl; cout<<"called by changeLine"<<endl; return *this; } int main(int argc, char *argv[]) { Line line1(12) ;//调用有参数的构造函数 Line* line2 = new Line;//调用无参数的构造函数 line1.setLength(10.0); line2->setLength(11.1); printLine(line1); cout<<"line1 "<<line1.getLength()<<endl; cout<<"line2 "<<line2->getLength()<<endl; Line line3 = line2->changeLine(23); cout<<"line3 "<<line3.getLength()<<endl; cout<<"obj count is "<<Line::getCount()<<endl; delete line2;//调用析构函数。程序结束时编译器自动释放line1 的资源,再次调用析构函数一次 }
object is created 12 1 objects left object is created 0 2 objects left copy Line 3 objects left line's length is 10 called by printLine object is deleted 2 objects left line1 10 line2 11.1 line's length is 23 called by changeLine copy Line 3 objects left line3 23 obj count is 3 object is deleted 2 objects left object is deleted 1 objects left object is deleted 0 objects left
最后,自行分析程序运行结果。
相关文章推荐
- C++ 字符串十六进制与十进制的互转
- C语言的学习
- C++新手之accumulate函数
- LeetCode 338. Counting Bits C语言
- C++委托实现(函数指针,function+bind,委托模式)
- c++ string主要的用法
- C++数据类型转换二
- C++静态成员
- C语言中int类型数的表示范围及形式
- C++学习笔记之七 复合类型---枚举
- 1005. Spell It Right (20)
- C语言宏定义用法注意事项与好处
- 回调函数
- HDU 5671 Matrix
- CUnit用法总结
- C语言模拟漏斗
- C++学习笔记之六 复合类型之共用体
- C++继承、虚函数处的面试题
- 关于“C++语言程序设计”书的一个类
- C++的动态库和静态库(dll)