概述
-
类是C到C++升华的重点,也是C++的基础。结课好久了才终于整理出来,真的比想象中麻烦很多。在学的时候感觉东西不多,但真正动手整理起来才发现角角落落,零零碎碎的东西真的不少。所以,到底什么是类,什么是对象:
①类的定义
class 类名{
private :
成员…
public :
成员…
protected :
成员…
} 对象名;
(1)私有成员在最前面给出时可以不标明private。
(2)成员函数可以写在类外,形式:
返回值类型 类名::成员函数名(…){……}
(3)私有成员只能在成员函数内被访问,公有成员可以在任何地方访问。
(4)类的成员函数可以访问当前对象的全部属性和函数,同时也可以访问其他对象的全部属性和函数。
(5)成员函数也可以重载和设置默认参数。
②构造函数
类名(…){……} 用来初始化对象
(1)系统会自己定义一个默认的构造函数,但什么也不做。☆要注意:如果你自己定义了任何一个构造函数,系统默认的无参构造函数就不存在了。当然,还可以自己写一个无参的构造函数。
(2)可以有多个构造函数,或者说构造函数可以重载。
(3)对象数组调用构造函数时初始化方式:
class A{
A() {……}//法1
A(int) {……}//法2
A(int,int) {……}//法3
} a[3]={2,A(1,2)};
此时,a[0]调用法2的构造函数,a[1]调用法3的构造函数,a[2]调用法1的构造函数,当然如果不存在法1的构造函数a[2]会调用默认构造函数。
③复制构造函数
类名(形参为对某一对象的引用){……}
(1)如果不自己定义,系统会生成默认的复制构造函数,默认的复制构造函数只能完成直接的复制。这个和默认的无参构造函数不同,这个默认的复制构造函数是一定存在的,无论是否自己写了新的复制构造函数。
(2)因为是以引用复制,所以最好是常引用,避免修改被复制的对象
(3)用法:
class A{
……
public :
A(const A & a) {……}
};
A b(a);
A b=a;
两种写法同样,当然,第二种不是赋值。
(4)复制构造函数起作用的三种情况:
1是直接调用,即(3)的使用。
2是如果某个函数的形参是个此类的对象,那么形参的生成时会调用复制构造函数,而其参数就是实参那个对象,通过复制构造函数产生一个实参对象的形参副本。
3是如果一个函数返回值类型是一个对象,则在函数返回时调用该类的复制构造函数用以生成一个对象,而其实参就是函数最后返回的那个对象。
(5)如上2,这样的函数在形参生成时会调用复制构造函数,从而花费大量时间。为避免在这里的开销,可以手动跳过这个环节,将函数中的形参直接写为原实参的引用(最好是常引用),这样就可以得到实参对象值的同时避免构造一个新的对象而花费太多时间了。
④类型转换构造函数
类名(一个参数){…}
(1)同样是一个构造函数,但形参变量只允许存在一个。作用是可以把实参自动转换成该类的一个(无名)对象,然后将其赋值给另一个对象。可以像普通的构造函数一样使用,但还可以直接赋值:
class A{
int a,b;
A x,y;
public :
A(){}
A(int s) { a=s; b=1; }
};
int main()
{
A d;
d = 6;
}
在正常情况下,d=6这样的赋值语句是会编译出错的,但因为存在这样一个只存在一个形参变量的构造函数,所以这时编译器会自动调用这个类型转换构造函数处理。
⑤析构函数
~类名(){…}
(1)同样的,当你没有自己写析构函数时,系统会自动定义一个缺省的析构函数。但如果你写了,系统就不会再生成了,也就是说,析构函数只有一个。
(2)析构函数会在对象消亡时自动被调用并做一些善后工作,尤其如释放内存空间。但如果是建立一个指针对象,而且指向一个new动态开辟的存储空间,那么将会在delete这个存储空间时调用析构函数。而且,程序中的所有临时对象生存期结束后都会调用析构函数,如对象作为函数返回值,或函数中形参是一个对象等。
⑥初始化成员列表
class A{
int x,y;
public :
class (int a,int b):x(a)y(b){}
};
和构造函数实现的功能类似但不同,具体作用和必须用初始化成员列表的情况之后再说。
⑦this指针
简单讲,就是在调用成员函数时用来指向当前类的对象的一个指针,作用就是用来找到指针指向的对象,避免同类多个对象时函数会“找不到对象”。
(1)一个类的所有对象共用一个this指针,当调用成员函数时(因为成员函数也是所有对象公用的),编译器会自动将调用成员函数的那个对象的初始地址赋值给this指针,让这个公用的成员函数找到它应该处理的对象。
(2)this指针的值就是对象的地址,所以如point1.x就可以写作this->x或(*this).x(括号不可省),当然,前提是当前this指针指向的是point1这个对象。但大多数情况不需要这样写(显示使用)。
(3)需要显示使用的情况
1在类的非静态成员函数返回类对象本身或者对象的引用时,直接return *this;而返回本对象的地址时直接return this;即可。
2形参与成员变量名相同时,应写作如this->x=x;而不能写作x=x;。说实话我觉得这条没用,形参仅在当前函数中起作用,起啥名不行非得起一样的名,后面加个123也可啊。不过考虑到大型的程序可能很复杂,或许还是有用的。
3避免对同一对象赋值,需要用到this指针,用以比较地址是否相同。如:if(&a!=this)或if(a!=*this)都可。
⑧静态成员变量和静态成员函数
顾名思义,在成员变量或成员函数前面加一个static后,它就成了静态存储的了。成员变量或成员函数静态存储以后就不再是某一个对象独有一份,而是所有该类对象所共有的了。
(1)静态成员变量的访问不再依赖某一对象了,即使不通过某一对象即可访问,类似于全局变量,但又不同。因为静态成员变量毕竟是成员变量,所以仍然只能在该类的成员函数中被访问,而因为它不依托于某一对象,所以就出现了静态成员函数以实现对它的访问。
(2)普通成员函数本身就是所有该类对象所共有的,但普通成员函数必须具体作用于某一对象。所以,静态成员函数仍然是所有对象共享的,但因为是静态的,所以对它的调用也不再依赖于某一对象,同时也不用具体作用于某一对象。恰好,静态成员变量就不是某一对象独有的,所以用静态成员函数处理静态成员变量再好不过了。
(3)正因为静态成员函数不会作用到某一个具体的对象,所以就无法访问非静态成员变量,同样也不能访问一个非静态成员函数(因为有可能通过非静态成员函数间接调用非静态成员变量,即使那个非静态成员函数没有调用非静态成员变量)。
(4)既然是为所有该类对象所共有,那么同成员函数一样,当用sizeof计算一个对象占用的字节数时就不会计算静态成员变量了。
(5)访问静态成员变量和静态成员函数方式
对于静态成员函数来说,仍然可以像普通成员函数一样通过某一对象对其调用,但对于调用静态成员函数的对象没有任何意义,但静态成员函数可以直接通过类调用,如A::cal();即可。而静态成员变量也可以在普通成员函数中被访问,调用时同样可以直接通过类调用,如:A::x;也可以通过某一对象调用,意义不同。
(6)静态成员变量是在类生成时就会存在,而普通成员变量只有在对象生成开始生命期开始时才会存在,也就是说即使该类没有任何对象存在,静态成员变量也是存在的,而其它成员变量没有意义。所以,静态成员变量应该在类的声明中进行一次声明操作或初始化操作,否则编译通过但会出错,形式如:
int A::cnt=0;
(7)通常对静态成员变量的操作会在构造函数和析构函数中进行,但这样的程序是存在缺陷的,当遇到:
1调用一个以该类对象作参数的函数时
2调用一个以该类对象作为函数返回值时
就会出现错误,因为这些情况会生成临时对象而自动调用复制构造函数,而复制构造函数中并没有对此静态成员变量的操作。解决方法就是自己编写合适的复制构造函数。
(8)静态成员函数中不可以使用this指针,因为静态成员函数并不具体作用于某一对象。
⑨常量对象,常成员函数,常引用
常对象就是对象的生成时标记const关键字,使得该对象的值变为只读的,不能对其修改。而常引用就是不可通过引用修改其引用的对象,但只要被引用的对象不是常量对象,仍然可以通过对象本身修改它的值。推广到常成员函数,就说明不可以通过常成员函数修改其作用的对象的,也不能调用非常量成员函数(因为有可能间接修改作用的对象的值)。注意这个“其作用的对象”,所以说,在常成员函数中可以修改静态成员变量,也可以调用静态成员函数的,因为静态成员变量不属于某一个对象。
⑩成员对象,封闭类和友元
(1)一个类的成员变量是另一个类的对象时,那么这另一个类的对象就叫做成员对象,而这个包含成员对象的类就是个封闭类。
(2)这样的封闭类在生成对象时,如果不自己写构造函数是会出错的,因为默认的构造函数不知道其中的成员对象该如何初始化。而解决的办法就是利用初始化成员列表的方法进行初始化,其中的成员对象就相应调用它对应类的构造函数。当然,如果成员对象对应类中存在无参构造函数的话初始化成员列表中不包括该成员对象也是可以的,或者说当不出现成员对象的初始化方式时系统会自动调用该对象所属类的无参构造函数。初始化成员列表中的参数可以是任意复杂的表达式(甚至是函数),只要返回值能够对应即可。
(3)封闭类调用构造函数和析构函数顺序
(4)友元函数
将本不属于当前类的函数前加上个friend即可将该函数生命为该类的友元函数,这个函数可以是个普通的全局函数或者别的类的成员函数,当这个函数被生命为当前类的友元函数后,就可以访问该类的私有成员了。实现的作用就是其它类可以通过友元函数直接访问的当前类的私有成员,当然接口就是这个友元函数。
(5)普通函数因为不会自动生成this指针,所以应该在函数参数中手动实现传递地址。而如果是另一个类的成员函数,那么声明友元函数的类应在后面,而成员函数作为友元函数的类应在前面,但因为前面的类中作为友元函数的成员函数会调用另一个类的成员变量(尤其私有),所以需要声明友元函数的类应在友元函数所在类之前进行声明。如:
class A;
class B{
int a,b;
public :
void cal(){…}
};
class A{
int x,y;
public:
friend void cal(){…}
};
这样,类B就成员函数cal就是类A的一个友元函数了。
(6)友元类
将一个类声明为另一个类的友元类,那么这一个类中的所有成员函数都相当于另一个类的友元函数,都可以访问另一个类的成员。
(7)友元类间不能传递,不能继承
不能传递就是指B是A的友元类,C又是B的友元类得了,但不能说C是A的友元类。继承以后再说。
以上就是类和对象的一些基本知识,或许不全,但在实际运用中都会逐一发现。类和对象部分的作业也做完了,虽然做完了也都ac了,但还是有种感觉,好多地方好多问题的处理方式还是面向过程的处理方式,既不是完全的面向过程的代码,也算不上面向对象的程序,说白了就是一种勉强解决了给出问题的半吊子程序。这样的程序解决这样的简单到不能再简单的问题自然没什么,甚至这些问题直接用面向过程的方法会简单的多,真正碰到复杂的需要面向对象的方法处理的问题时都得歇菜。但毕竟,语言课本就是基础课,知道怎怎么写就好,甚至只是简单的应用也会出现这样那样的问题,而想要真正用好,还有好长的路要走呢。自以为学了好多东西了,其实在实际问题面前还是小菜鸡一个。。。
最后
以上就是笑点低水池为你收集整理的class类和对象的全部内容,希望文章能够帮你解决class类和对象所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复