1.类的实现
一般有2种,一种在类定义时完成对成员函数的定义,一种是在类外进行定义。
类外实现 函数类型+类名::函数名
代码短就定义在类内,类内默认为inline函数(内联函数)
2.类的成员
如果在类的定义中既不指定private,也不指定public,则系统就默认为是私有的。
被声明为私有的(private)成员,只能被本类中的成员函数引用,类外不能调用(友元类除外)。
被声明为公用的(public)成员,既可以被本类中的成员函数所引用,也可以被类的作用域内的其他函数引用。
用protected声明的成员称为受保护的成员,它不能被类外访问(这点与私有成员类似),但可以被派生类的成员函数访问。
3.关于继承
继承方式包括
public(共有的),private(私有的)和protected(受保护的)
如果不写继承方式默认为private
struct(公有)和class(私有)
子类:父类
共有的和保护的可以被继承,私有的不行
例子:父类people派生student类
#include<iostream>
using namespace std;
class people
{
public:
void setname(char* const name)
{
m_name=name;
}
void setage(int age)
{
m_age=age;
}
char *getname()
{
return m_name;
}
int getage()
{
return m_age;
}
private:
char *m_name;
int m_age;
};
class student:public people
{
public:
void setscore(float score)
{
m_score=score;
}
float getsocre(){
return m_score;
}
private:
float m_score;
};
int main()
{
student stu;
stu.setage(16);
stu.setname(“小明”);
stu.setscore(98.6);
cout<<stu.getname()<<"的年龄是"<<stu.getage()<<"成绩是"<<stu.getsocre()<<endl;
return 0;
}
people是基类,student是派生类。
4.构造函数
构造函数分为构造函数和复制构造函数。构造函数可以有多个,而复制构造函数只能有一个,因为复制构造函数的参数只能是当前类的一个对象的引用,参数表是固定的,无法重载,若用户没有定义自己的复制构造函数,系统会自动生成一个复制构造函数,其作用是将参数值赋予当前的对象.若用户自己定义了复制构造函数,系统则不会生成默认复制构造函数。
构造函数名与类名相同,通常被声明为公有函数,无返回类型关键字。只要类中有构造函数,编译器就会在建立对象的地方自动插入对构造函数调用的代码。因此,我们通常会构造函数在对象创建时自动调用。如果类中没有写构造函数,编译器会自动生成一个默认的无参构造函数,其参数列表和函数体都为空。如果类中声明了构造函数,编译器就不再生成隐含构造函数。
(1).无参构造函数
#include<iostream>
using namespace std;
class A{
public:
A(){
cout<<"hello"<<endl;
}
};int main(){
A a;
return 0;
}
(2).有参构造函数
#include<iostream>
using namespace std;
class student
{
public:
student(char* name,int age){
m_name=name;
m_age=age;
}
void show(){
cout<<m_name<<"的年龄是"<<m_age<<endl;
}
private:
char* m_name;
int m_age;
};
int main()
{
student stu("小明",15);
stu.show();
return 0;
}
(3).构造函数的重载与调用
#include<iostream>
#include<cstdio>
using namespace std;
class test
{
public:
test()
{
m_a=0;
m_b=0;
cout<<"无参构造函数被调用"<<endl;
}
test(int a)
{
m_a=a;
m_b=0;
cout<<"有1个参数的构造函数被调用"<<endl;
}
test(int a,int b)
{
m_a=a;
m_b=b;
cout<<"有2个参数的构造函数被调用"<<endl;
}
void printf(){
cout<<"a="<<m_a<<",b="<<m_b<<endl;
}
private:
int m_a;
int m_b;
};
int main(){
test a;
test b(1);
test c(2,3);
a.printf();
b.printf();
c.printf();
return 0;
}
无参构造函数被调用
有1个参数的构造函数被调用
有2个参数的构造函数被调用
a=0,b=0
a=1,b=0
a=2,b=3
5.析构函数
析构函数是一种特殊的成员函数,没有返回值,不需要程序员显式调用,而是在销毁对象时自动执行。析构函数的名字和类名相同,而析构函数的名字是在类名前面加一个“~”的符号。
#include<iostream>
#include<cstdio>
using namespace std;
class demo
{
public:
demo()
{
cout<<"构造函数被调用"<<endl;
}
~demo()
{
cout<<"析构函数被调用"<<endl;
}
};
int main()
{
cout<<"begin"<<endl;
{
demo d;
}
cout<<"end"<<endl;
return 0;
}
begin
构造函数被调用
析构函数被调用
end
6.关于类的嵌套
(构造子类,必定先析构父类)
(析构父类,必定先析构子类)
类的嵌套先进行对成员的构造,在函数体
析构则是先函数再成员
一个综合的小例子:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
class Apple{
public:
Apple(){
printf("apple constructor.......\n");
}
~Apple(){
printf("apple destructor.........\n");
}
};
class person{
public:
person(){
printf("person constructor.......\n");
}
~person(){
printf("person destructor.......\n");
}
person(int age,char name[]){
this->age=age;
strcpy(this->name,name);
}
void show(){
printf("person[name=%s,age=%d]\n",this->name,age);
}
protected:
int age=0;
private:
char name[10];
Apple apple;
};
class student : public person{
public:
student(){
printf("student constructor.......\n");
}
~student(){
printf("student destructor.......\n");
}
student(int age,char name[],int sid):person(age,name),sid(sid)
{
}
private:
int sid=0;
};
int main(){
person p;
return 0;
}
apple constructor…
person constructor…
person destructor…
apple destructor…
apple在person中,先person在apple
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
class person{
public:
person(){
printf("person constructor.......\n");
}
~person(){
printf("person destructor.......\n");
}
person(int age,char name[]){
this->age=age;
strcpy(this->name,name);
}
void show(){
printf("person[name=%s,age=%d]\n",this->name,age);
}
protected:
int age=0;
private:
char name[10];
};
class student : public person{
public:
student(){
printf("student constructor.......\n");
}
~student(){
printf("student destructor.......\n");
}
student(int age,char name[],int sid):person(age,name),sid(sid)
{
}
private:
int sid=0;
};
int main(){
student p1,p2;
return 0;
}
person constructor.......
student constructor.......
person constructor.......
student constructor.......
student destructor.......
person destructor.......
student destructor.......
person destructor.......
次序
(1)调用基类构造函数(父类),调用顺序按照他们被继承时声明的顺序(从左到右)
(2)如果参数是类类型,调用复制构造函数,把实参值复制到形参中
(3)对于派生类的新增成员对象的初始化,调用顺序按照他们在类中的声明顺序
(4)执行派生类的构造函数体
7.复制构造函数:
是和一种特殊的构造函数,具有一般构造函数的所有特性,其形参是本类的对象的引用。其作用是使用一个已存在的对象(由参数指定),去初始化同类的一个新对象。
==如果程序员没有定义类的复制构造函数,系统就会在必要时生成一个隐含的复制构造函数。
会产生复制构造函数的三种情况
(1)在函数中类型是类会产生复制
(2)赋值 例:point p3; p3=p2
(3)函数的返回值是类(最好加static)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
class Point {
public:
Point(int x, int y){
this->x = x;
this->y = y;
}
Point(const Point& p){
printf("copy:addr--->p=%x\n", &p);
x = p.x;
y = p.y;
}
void show(){
printf("show:addr=%x, x=%d, y=%d\n", this, this->x, this->y);
}
static void test(Point p){
printf("test:addr=%x, x=%d, y=%d\n", &p, p.x, p.y);
}
private:
int x=0, y=0;
};
Point g(){ //函数内部不调用复制构造函数,直接把a作为临时对象返回
Point a(3, 4);
printf("a.addr=%x\n", &a);
return a;
}
int main() {
Point p1(1,2);
p1.show();
Point p2(p1);
p2.show();
Point p3 = p2;
p3.show();
Point p4 = g(); //临时对象是看作常对象来处理的,因此要求复制构造函数的参数是const的
p4.show(); //p4和g()返回的临时对象是同一个对象
g();
return 0;
}
show:addr=4353db50, x=1, y=2
copy:addr—>p=4353db50
show:addr=4353db48, x=1, y=2
copy:addr—>p=4353db48
show:addr=4353db40, x=1, y=2
a.addr=4353db38
show:addr=4353db38, x=3, y=4
a.addr=4353db58
8.动态内存分配
在C++中,动态内存分配技术可以保证程序在运行过程中按照实际需要申请适量的内存,使用结束后还可以释放,这种在程序运行过程中申请和释放的存储单元也称为堆对象,也称为建立和删除。
在C++中,建立和删除堆对象使用的两个运算符:new和delete
new 数据类型(初始化参数列表);
该语句在程序运行过程中申请用于存放指定类型数据的内存空间,并根据初始化参数列表中给出的值进行初始化。如果内存分配成功,new 运算符返回一个指向新分配内存的首地址;如果申请失败,会抛出异常。
如果建立的对象是一个基本数据类型,初始化过程就是赋值,例如:
int * ap = new int(2);
动态分配了用于存放int型数据的内存空间,并将初值2存入该空间中,然后将首地址赋给指针ap。对于基本数据类型,如果不希望在分配内存后设定初值,可以把括号省去。例如:
int * ap = new int;
如果保留括号,但括号中不写任何数值,则表示用0对该对象初始化,例如:
int * ap = new int();
delete 指针名 ;
运算答delete用来删除由 new 建立的对象,释放指针所指向的内存空间。如果删除的是对象,该对象的析构函数将被调用。对于用new 建立的对象,只能使用delete进行一次删除操作,如果对同一内存空间多次使用delete进行删除,将会导致运行时错误。
new 也可以创建数据类型的对象,一般语法如下:
int *p = new int[10]; //仅创建,不初始化元素值
或
int *p = new int[10] (); //()内不能放任何参数,功能是初始化每个元素为0
class Point {
public:
Point():x(0),y(0){
printf("Default Constructor is called.\n");
}
Point(int x, int y):x(x),y(y){
printf("Constructor is called.\n");
}
~Point(){
printf("Destructor is called.\n");
}
private:
int x, y;
};
int main(){
printf("Step one:\n");
Point *ptr1 = new Point();
delete ptr1;
printf("Step two:\n");
ptr1 = new Point(1, 2);
delete ptr1;
return 0;
}
Step one:
Default Constructor is called.
Destructor is called.
Step two:
Constructor is called.
Destructor is called.
为节省存储空间,C++创建对象时仅分配用于保存数据成员的空间,而类中定义的成员函数则被分配到存储空间中的一个公用区域,由该类的所有对象共享。
用了new的就创建在堆中,只能用deldete删除(否则内存泄漏)