1.无论是数据表示还是成员函数,都可以在类的共有部分和私有部分之中进行声明;
但是数据的隐藏,一般来说,在私有部分之中声明数据表示,在共有部分之中声明组成类接口的成员函数;
2.不必在类的声明之中使用关键字private,类对象已经默认访问;
3.一般来说,类的声明放在头文件之中,将成员函数的定义放置在单独的一个源代码文件之中;
4.cout和Cerr的区别:
也就是说cout的输出可以重定向到一个文件中,而cerr必须输出在显示器上。
cout是标准输出流,与cerr的区别在于cerr不经过缓冲区,直接向显示器输出信息,而cout中的信息存放在缓冲区,缓冲区满或者遇到endl时才输出。
5. 函数定义位于类的声明中的函数将自动转换成为内联函数,一般来说,只将短小的成员函数作为内联函数;
另一种方法就是,在类的声明之中声明函数原型,然后在类声明的后面紧跟函数的定义并且使用inline限定符;
6.定义成员函数时,使用作用域解析操作符(::)来标识函数所属的类
7.char* class_name::function_name //表示函数返回一个char指针(即返回类型放在类名之前)
8.Student *s1,s2,s3("Jack",90,80,70); //声明了一个Student的指针类型s1,声明并定义了Student类型的变量s2,s3,后面的两种定义方式都是隐式的,只不过s2的定义如上述 默认构造函数 所述,容易使人产生 这里仅仅是声明而不是定义的错觉。
9.构造函数和析构函数
演示实例:
1)在头文件student.h中声明类Student
1 //student.h -- Student class interface 2 //version 00 3 #ifndef STUDENT_H_ 4 #define STUDENT_H_ 5 6 #include <string> 7 8 class Student{ //class declaration 9 private: 10 std::string name; 11 int ch; 12 int en; 13 int math; 14 float average; 15 void count(){ 16 average = (ch + en + math+0.0F)/3; 17 } 18 public: 19 Student(); 20 Student(std::string name,int chScore,int enScore,int mathScore); 21 ~Student(); 22 void setName(std::string name); 23 void setChScore(int score); 24 void setEnScore(int score); 25 void setMathScore(int score); 26 void show(); 27 }; 28 29 #endif
2)在文件student.cpp中实现类方法
1 //student.cpp -- implementing the Student class 2 //version 00 3 #include <iostream> 4 #include "student.h" 5 6 Student::Student(){ 7 cout<<"您好呀! 新同学,你叫什么名字?"<<endl; 8 } 9 Student::Student(std::string n,int chScore,int enScore,int mathScore):name(n),ch(chScore),en(enScore),math(mathScore){ 10 cout<<"您好呀! "<<name<<",很高兴见到你!"<<endl; 11 } 12 Student::~Student(){ 13 cout<<"再见! "<<name<<"."<<endl; 14 } 15 void Student::setChScore(int score){ 16 Student::ch = score; 17 } 18 void Student::setName(std::string n){ 19 Student::name = n; 20 } 21 void Student::setEnScore(int score){ 22 en = score; 23 } 24 void Student::setMathScore(int score){ 25 math = score; 26 } 27 void Student::show(){ 28 Student::count(); 29 ios_base::fmtflags orig = cout.setf(ios_base::fixed,ios_base::floatfield); 30 std::streamsize prec = cout.precision(1); 31 cout<<name<<" 同学的语文成绩为"<<ch<<"分,数学成绩为"<<math<<"分,英语成绩为"<<en<<"分,平均成绩"<<average<<"分"<<endl; 32 cout.setf(orig,ios_base::floatfield); 33 }
3)在main方法中调用
1 //visual studio 2010 --main program 2 3 #include<iostream> 4 #include "student.h" 5 6 7 int main() 8 { 9 Student *s1,s2,s3("Jack",90,80,70); 10 s1 = new Student("Sam",90,95,100); 11 s1->show(); 12 13 s2 = Student("Sue",85,90,95); 14 s2.show(); 15 s3.show(); 16 17 s1 = new Student("Joe",91,92,93); 18 s1->show(); 19 delete s1; 20 21 return 0; 22 }
运行结果:
运行结果: 您好呀! 新同学,你叫什么名字? 您好呀! Jack,很高兴见到你! 您好呀! Sam,很高兴见到你! Sam 同学的语文成绩为90分,数学成绩为100分,英语成绩为95分,平均成绩95.0分 您好呀! Sue,很高兴见到你! 再见! Sue. Sue 同学的语文成绩为85分,数学成绩为95分,英语成绩为90分,平均成绩90.0分 Jack 同学的语文成绩为90分,数学成绩为70分,英语成绩为80分,平均成绩80.0分 您好呀! Joe,很高兴见到你! Joe 同学的语文成绩为91分,数学成绩为93分,英语成绩为92分,平均成绩92.0分 再见! Joe. 再见! Jack. 再见! Sue. 请按任意键继续…
分析:
我们来一步一步的分析代码执行流程, Student *s1,s2,s3("Jack",90,80,70); 这一句代码声明了三个变量,并且定义了其中的两个!!! 这里声明了一个Student的指针类型s1,声明并定义了Student类型的变量s2,s3,后面的两种定义方式都是隐式的,只不过s2的定义如上述 默认构造函数 所述,容易使人产生这里仅仅是声明而不是定义的错觉。 s2 = Student("Sue",85,90,95); 现在再来看这一句,这里产生了一个上述临时对象。这个临时对象会将其内部的值复制到s2的空间中,其后自动销毁。那么为什么不让s2的引用指向这个新建立的对象呢?这就要从变量内存上分析,由于我们的s2和s3是在栈中分配的内存,而栈又会随着方法的退出而销毁,就导致了栈中的对象是不稳定的。如果我们采用指向的方式,那么我们就不知道什么时间我们指向的栈帧就会撤销,这是我们再去访问我们的方法就会报错了。 s1 = new Student("Joe",91,92,93); 由于s1是在堆中分配的内存,所以我们才能在new时直接传递引用,但是这种写法是一个不好的编程习惯。因为我们在给指针重新分配内存的时候并没有释放原空间,如果产生大量这种代码将会造成内存溢出 最后,为什么先释放的是s3,后释放的是s2呢?是由于栈的FILO性质导致的。
9.this指针 //个人理解,感觉相当于python类中的self
1 例:同一个方法,两个对象进行比较,返回最大的对象的引用。(引用直接作用于原始数据,可以提高比较的效率) 2 3 例如: 4 函数原型:const Stock & topval(const Stock & s)const; 5 6 有以下两条语句等价: 7 8 top = stock1.topval(stock2); 9 top = stock2.topval(stock1); 10 11 12 const Stock & Stock::topval(const Stock & s)const 13 { 14 if (s.total_val > total_val) 15 return s; 16 else 17 return *this; //由于stock1没有别名,this指针解决了这个问题; 18 //this指针用来指向调用这个成员函数的对象。 19 //一般来说,所有的类方法都将this指针设置为调用它的对象的地址; 20 //this是对象的地址,而*this才是对象本身. 21 }
10.当程序创建而未被显式初始化的类对象的时候,总是调用默认构造函数;
11.创建对象数组其实和创建数组的形式差不多,初始化的方式也类似;
12.要创建数组对象,那么这个类必须要有默认构造函数。这是因为创建数组元素的时候,是调用默认构造函数创建的,而花括 号中的构造函数将创建临时对象,再将此临时对象复制给相应的数组元素中。
13.操作符重载:
对象1.operator + (对象2) //重载+
对象1.operator - (对象2) //重载-
也可以直接+/-
对象1+对象2
对象1- 对象2
1 #例如: 2 3 class Time 4 { 5 .... 6 7 public: 8 9 ... 10 Time operator+(const Time & s)const; //声明 重载+ 11 }; 12 13 14 Time Time::operator+(const Time & s)const //定义
15 { 16 ... 17 } 18 19 20 int main() 21 { 22 ... 23 Time s1; 24 Time s2; 25 Time s3; 26 s3= s1+s2; //对象直接相加 27 ... 28 }
推广情况:多个对象相加的形式,其实都是一样的操作
例如:
t4 = t1 + t2 +t3
等价于:
t4 = t1.operator+(t2.operator+(t3))
14.当需要将参数类型转换成类类型的时候,将默认隐式地调用只接受一个参数的构造函数来进行转换;
如果在类声明的构造函数的原型头部添加关键字explicit ,则需要进行显示的转换;
同理,如果要将类类型转换为其他的参数类型的话,则需要在类声明之中声明一个执行这种转换的转换函数。
具体如下:
1 /* 2 1)其他的类型转换为类类型: 3 4 String tone; 5 tone = 4;//隐式转换 6 7 若在声明中使用explicit关键字(如:explicit String(double x)),则: 8 9 String tone; 10 tone = String (4);//显式转换 11 12 2)类类型转换为其他的类型: 13 14 构造转换函数,具体的形式: 15 16 operator double()const; 17 18 转换函数注意以下几点: 19 1.转换函数必须是类方法啊; 20 2.转换函数不能指定返回类型; 21 3.转换函数不能有参数; 22 23 */
浙公网安备 33010602011771号