一、构造方法
观察:
class A{
float x,y;
public:
//构造1:构造函数可以指定参数的缺省值
A(float a,float b=1){ x=a; y=b; }
//构造2: 无参
A() { x=0; y=0; }
void Print(void) {cout<<x<<'\t'<<y<<endl; }
};
int main(void){
A a1, a2, a3(3.0, 7.0);
a1.Print();
a2.Print();
a3.Print();
}
1、因为构造2的存在,构造1不能给俩参数都有默认值;
2、这个写法是错的: A(float a=1,float b){ x=a; y=b; } 第一个参数不能有默认值(规定)
3、全局对象,在main函数执行之前调用构造函数
4、域不赋默认值 则是随机值,java是0或者false···
读个程序:
发现,两次调用f();只有一个静态A存在,不是覆盖上的,而是根本没调用第二次构造.和java一样,
一个方法不论被调几次,其内部的静态变量都是初始化1次,并在全局实例析构之前析构
再说一遍。。。new 运算符:返回地址 用new运算符产生的动态对象,在不再使用这个对象时,必须用delete运算符来释放对象所占用的存储空间
demo:
class A{
public:
int x,y;
//this指针之前没说,就是java的this
A(int x,int y){this->x = x;this->y = y;}
void printx();
//一种初始化方法:
A(int a,int b,int c):x(a),y(b+c){
//当需要初始化的数据成员较多时,这种方式更显其优越性.
}
//可以在这声明一个函数头,体在下面
A();
//析构函数:函数名与类名相同,前面加上字符 ~ 不能有参数、返回值,不指定函数类型,和构造方法一样 也 有默认的析构函数
//一个类中,只能定义一个析构函数,析构函数不允许重载,回收对象时由系统自动调用
~A(){
cout<<"a instance deinit";//方法体可以写类外,像构造方法一样
}
(一般地,)调用析构函数的次序正好与调用构造函数的次序相反。
};
A::A(){
cout<<"NO para"<<endl;
}
void A::printx(){
cout<<x<<endl;
}
int main(){
A*a;
a = newA(10,10);
A a2(10,10,10);
A*b = & a2;
a->printx();
b->printx();
delete a;
// 用new的必须delete,不new的不能delete
// 直接定义的变量是定义在栈里,随着函数的弹栈而释放
// new出的东西定义在堆里,需要手动释放,系统不能回收new分配的动态存储空间
//引用变量,下面的 a45:
A & a45 = a2;
// 注意:指针用->,引用用 .
a45.printx();
//C++与java相比,多了个指针,引用也是有的。声明格式: className & varietyName = instance
}
再看一个demo学习:
class Str{
char *Sp; int Length;
public:
Str(char *string){
if(string){
Length=strlen(string);
Sp=newchar[Length+1]; //在构造函数中将成员数据指针指向动态开辟的内存
strcpy(Sp,string); //用初值为开辟的内存赋值
}else
Sp=0;
}
void Show(){
cout<<Sp<<endl;
}
~Str(){
if(Sp)
delete []Sp; //析构函数,当释放对象时收回用new开辟的空间
}
};
对象如果用new运算符开辟了空间,则在类中应该定义一个析构函数,并在析构函数中使用delete删除由new分配的内存空间
int main(){
Str s1("Study C++");
s1.Show();
}
下面这个demo,可以不看
class A{
public:
int x,y;
A(int x,int y):x(x),y(y){
cout<<"an A instance init "<< endl;
}
~A(){
cout<<"an instance deinit"<<endl;
}
void printx();
};
void A::printx(){
cout<<x<<endl;
}
class B{
public:
int x,y;
B(int x,int y):x(x),y(y){
cout<<2<<"a B instance init"<<endl;
}
~B(){
cout<<"an instance deinit"<<endl;
}
void printx();
};
void B::printx(){
cout<<x<<endl;
}
int main(){
A*a =new A(10,20);
delete a;
a->printx();
((B*)a)->printx();
自己跑起来看看,打印:
an A instance init
an instance deinit
10
10
1 为什么打印的是10?
原理要从堆内存的实现讲起,从堆上动态申请的内存,在 delete 后,只是将内存块重新添加到当前进程的内存块描述空闲块链表中,内存块内容并不会清空,除非别的线程申请了这个块写入了其他内容,所以10保持不变。
2 为什么 delete 后还能访问,甚至转为 B* 也能访问?
实际上,代码编译后根本就没有类、结构体这些概念,a->printx() 实际上的代码变为:a 处地址,偏移0后,取出地址上的值,打印。打印 y 的话就是 a 偏移 4 字节处,取值。程序根本不知道类这些东西,只是给程序员看的。
}
关于 C++ 中的面向对象
封装和 java 差不多
继承:C++ 支持继承多个类,但是很少会继承多个类,因为基本等于作死,项目大了之后,多继承问题非常多,不如用接口、协议。
多态:一个类的某个成员函数,声明时 前面加上 virtual,后面 = 0,不用写函数实现,子类实现后会自动动态绑定。和 java 多态思想差不多,不多说。