文章目录
构造函数:
对象和内存的关系:
有空间不一定有对象。
有对象一定有空间。
class Empty
{
};
int main()
{ //看不见构造函数,是因为,只要程序运行,系统会调用默认构造函数,创建对象
Empty e; //占位符,有对象,一定要有空间
cout<<sizeof(e)<<endl;
Empty *p = &e; //如果e没空间,指向哪?
}
1.1 构造函数的用途:
1.创建对象
2.初始化对象中的属性。
3.类型转换
类型转换:
class Int
{
public :
Int(x = 0):value(x){}
}
int main()
{
Int ix (12);
int a = 200;
ix = a; //赋值给ix的时候,会创建调动构造函数一个无名对象,将a传给ix(完成了类型转换。)
}
- 构造函数的返回值是构造函数创建的对象。(系统分配的空间中)
- 在程序运行的时,当新的对象被建立,该对象所属的类的构造函数自动被调用,在该对象的生存期中也只调用一次。
不可以重新构建c1对象,因为没调动一次构造函数,就会创建一个对象/
但是new(&c1) Comlex(200,300)
,可以让c1再构建一次。 - 【构造函数可以重载】,严格意义上讲,类中定义了多个构造函数:,(每个构造函数中可以完成不同的属性,生可带来,死了带不走,直接析构了)
class Complex
{
double Real;
double Image;
public:
Complex(){}
Complex(double r,double i)//函数重载
{
Real = r;
Image = i;
}
fun(Complex cx)
{}
}
int main()
{
Complex c1; //调用的Complex()默认构造函数
Complex c2(1.1,2.2);//调用的是Complex(double r,double i)
Complex c3();//一定要写数据,负责会被认为是函数的声明
complex c4{};//可以,列表的初始化方案
fun(Complex(2,3));//构造函数一定要获取某个空间,在某个空间构造对象。
}
Complex c3();
/一定要写数据,负责会被认为是函数的声明
4. 构造函数可以在类中定义,也可以在类外定义
5. 如果类说明中没有给出构造函数,C++编译器会自动生成一个缺省构造函数
complex(){}
只要我们自己定义一个构造函数,系统就不会自动生成缺省构造函数,
如果构造函数不带参数或者带参数没有缺省值(如下图),就会被认为缺省构造函数。
关键字:sizeof(),编译期确定大小。
构造函数一定要获取某个空间,在某个空间构造对象。
1.2 构造函数具有this指针:
class Complex
{
private:
double Real;
double Image;
public:
Complex(){}//Complex(Complex *const this)
}
Complex c1; Complex(&c1)
1.3 结构体和类的区别:
struct 内默认为public
class 内默认为private
struct str
{
int a;
char b; //可以通过str s; s.a =1,s.b = "s";进行初始化
}
class Com
{
private:
int real; //private: int real;int image;
public : //public: Com(int r = 0,int i = 0)
int image // Com c3{3,4} Com c2 = {2}; 均可初始化,从右往左
}
int main()
{
Com c1 = {1}; //错误,
Com c2 = {2}; //错误 必须是成员属性均为公有,才可以初始化,
//系统会自动提供一个有两个形参的构造函数。
// Com(int ,int);
Com c3{3,4} ;
}
- 缺省构造函数是否有【形参】取决于定义的成员属性公有还是私有
公有如下图:有几个属性,缺省构造函数就有几个形参,并且每个形参的缺省给为0
私有如下图:
1.4 对构造函数使用初始化列表:
初始化和设计顺序必须相同。
class Complex
{
private:
double Real;
double Image;
public:
Complex():real(0),image(0)//成员初始化列表
{}
Complex(double r,double i):real(r),image(i)
{
Real = r;
Image = i;
}
}
可能出现的问题:
class Complex
{
private:
int real;
int image;
public:
Complex(int x) :image(x), real(image) {}//read(x),image(real)可以
void print()
{
cout << "real:" << real << "image:" << image << endl;
}
};
int main()
{
Complex c1(10);
c1.print();
}
列表初始化的顺序最好和属性定义的顺序相同,否则如图。
类中定义了多个构造函数,:向现实靠拢,每个人的出身不同,所带的属性也就不同
1.5 构造函数可以用引用的方式返回。
int &Real() ,相当于返回this指针指向对象的别名
{
return real;
}
析构函数:
【对象的生存期满了就会自动调用。】
用构造函数创建对象后,程序负责跟踪对象,当对象使用结束,系统就会调用析构函数,完成对对象的清理。
注意:
1.析构函数不可重载,无可见参数
2.析构函数如果自己没有实现,系统会默认生成一个。
语法:~函数名()
- 局部对象,在函数调用点处创建对象,函数结束,对象被析构,先创建的后析构
- 静态局部对象(在数据区):等这个程序结束的时候才会被析构。
- 全局对象(全文见可见):当程序进入main之前,对象已经定义,整个程序结束,对象才会析构调。(静态全局对象,本文件可见。)
Complex c1(1,1);
int main()
{
cout<<"begin main"<<endl;
Complex c1(10);
cout<<"end main:"<<endl;
return 0;
}
Complex c3(3,3);
不论全局函数在哪,在进入主函数之前都会定义好,
4.块内对象:进入块中调用构造函数创建对象,从块中出来,析构对象。
//只在块中有效
int main()
{
cout<<"begin main"<<endl;
Complex c1(1,1);
cout<<"begin 块:"<<endl;
{
Complex c2(2,2);
} //这个位置块内对象析构
cout<<"end 块"<<endl;
cout<<"end main"<<endl;
return 0;
}
- 不能控制生,但可以控制死。(可以主动调用析构函数,析构对象)
一般不要这样,系统无法记录析构函数调动了几次,因此自己析构了,但是系统还会再析构。
int main()
{
Complex cs(1,1);
//
cs.~Complex(); //主动调动。
return 0;
}
5.动态创建的对象, 使用new创建对象,delete释放对象
int main()
{
int n = 5;
// Complex* xp = new Complex[n]{{1,2},{},{4,5},{6,7}};//1.申请堆空间2.创建对象(调用的是有参的构造函数)
// {}调用的是无参构造函数
Complex* xp = new Complex{};
// xp -> print();
for(int i = 0;i<n;i++)
{
xp[i].Print(); //申请了一组对象
}
// delete xp; //1.析构对象,将内存还给申请空间,2.将申请的空间还给堆
delete []xp;
xp = nullptr;
}
有空间不一定有对象,必须要调用构造函数创建对象。
所以,不能调用对象的方法。
有对象才可以对空间进行操作。
如下:
int main()
{
Complex *xp = (Complex*)malloc(sizeof(Complex));//只申请空间,没有兑现
//调动构造函数再new指定的xp申请的空间中构建对象
new (xp) Complex{2,3};//定位new,构建对象。这样
xp->print(); // xp才可以访问对象的方法。
xp->~Complex();//主动析构对象。
free(xp);
xp = NULL
}
所以malloc + 定位new实现了 new 的功能。
成员方法的设计
- 常对象只能调动常方法
- 普通对象可以调动普通方法,也可以调动常方法
- 普通对象优先调用普通方法
原因:对象调用类中的方法,本质上是将对象的地址传给this指针,因此对象被const修饰,在被this指针指向的时候,能力只能缩小,不能扩大。
当类中的成员属性设置为私有的时候,可以通过成员方法访问或修改成员属性。
如:
class Complex
{
private:
int num;
int age;
public:
int set_num(int n)
{
num = n;
}
// int set_age(const Complex *const this ,int a)
int set_age(int a) const //常方法 但是导致无法对age进行赋值
{
age = a;
}
int get_num() const //在获取数据的时候加const,只读不写
{
return num;
}
int get_age() const
{
return age;
}
}
int main()
{
Complex cc;
cc.set_num(10); //通过成员方法。。。
const Complex ss; //常对象 ,约束的是指向能力,一般用于获取数据
ss.set_age(20); //只能访问常方法
cc.set_age(10);//也可以。能力缩小 导致无法写入数据
//在获取数据的时候加const,只读不写
ss.get_age();
cc.get_num();
}
常方法里只能调动其他常方法。
(主要就是this指针的可访问性权限,指向的能力)
如下:
class Complex
{
private:
int num;
int age;
public:
int set_num(int n)
{
num = n;
}
//int print(const Complex *const this,int n)
int print(int n)const
{
//
this->set_num(n)//错误,因为set_num(n) 相当于set_num(this,n),print的this 放入sert_num,能力被扩大
}
// int set_age(const Complex *const this ,int a)
int get_num() const //在获取数据的时候加const,只读不写
{
this->print();// print(this) 常方法可以调用常方法
return num;
}
int get_age()
{
this->print(); //普通方法也可以调用常方法,因为可以this指针传给print的this,能力被缩小。
return age;
}
}