会飞的蝌蚪君

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

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 */

 

 

 

posted on 2018-03-29 18:51  会飞的蝌蚪  阅读(118)  评论(0)    收藏  举报