C++学习笔记
一直没有系统地学习C++这门语言,从今天现在开始学习一下,由于前部分语法和C基本一样,我们从类开始进行学习
类
- 类是面向对象的最重要的概念之一
- 利用类实现数据的封装,隐藏,继承和派生
- 类成员的默认访问权限是private
public | private | protected |
---|---|---|
公有 | 除了类体内的成员函数可以访问类声明的私有变量外,任何外部函数都不能访问(友元函数除外) | 受保护的成员可以被类体内的成员函数和派生类的成员函数访问 |
class Date{
public:
void input();
void output();
void setDate(int y,int m,int d);
private:
int year;
int month;
int day;
};//注意分号不能省略
void Date::input()
{
cout<<"请输入合法的年月日";
cin<<year;
cin<<month;
cin<<day;
}
void Date::setDate(Date* const register this,int y,int m,int d)
{
//这里还有一个参数this,传过来的是该类的一个指针
this->year=y;
this->month=m;
this->daty=d;
}
构造函数
- 名字和类一模一样
- 没有返回值,声明和定义都不允许指定返回类型
- 构造函数在声明类对象的时候由系统自动调用执行
- 构造函数的访问权限应设定为public
- 用户可以对构造函数进行重载为类提供多个构造函数
- 当有了构造函数之后系统就不会生成默认构造函数了
析构函数
当对象消亡时有系统自动调用执行
//格式
~类名();
对象指针
#include <iostream>
#include <cstring>
using namespace std;
class Elec //声明Elec类
{
public:
char ename[50];
char mnum[20];
int num;
char addr[20];
Elec(){}; //声明构造函数
Elec(char p[],char q[],int l,char m[]) //定义带参数的构造函数
{
strcpy(ename,p);
strcpy(mnum,q);
num=l;
strcpy(addr,m);
};
void show()
{
cout<<ename<<" "<<mnum<<" "<<num<<" "<<addr<<endl;
}
};
int main()
{
Elec elec("Apple iphone","3GS",568948,"广东"); //定义对象
int *p=&elec.num; //定义指向整型数据的指针变量p,使其指向数据成员num
cout<<*p<<endl; //输出p所指的数据成员
void(Elec::*pfun)(); //定义指向Elec类公有成员函数的指针pfun
pfun=&Elec::show; //使pfun指向Elec类的公有成员函数show
(elec.*pfun)(); //调用对象elec中pfun所指的成员函数
return 0;
}
静态成员与友元
静态数据成员
- 在c++中提供了一种机制,让类的不同数据成员可以共享数据,由所有的类的对象共同维护和使用该数据
- 静态数据成员的生存期与整个程序相同
- 静态数据成员被声明为私有的,但是必须在类外进行初始化
- 可以通过对象.静态变量成员名进行访问静态数据成员
- 由于静态变量不属于任何一个对象,也可以通过类名进行访问—类名::静态数据成员名
静态成员函数
- 静态成员函数中没有this指针,不能默认的访问本类的非静态数据成员
- 也可以通过类名进行调用
友元
顾名思义,友元就是朋友元,成员函数做友元,类做友元。
友元函数可以在类的任何一个部分进行声明
class Building{
friend void func();
friend class goodGay;
public:
private:
};
借用friend关键字进行声明友元函数
友元破坏了函数的封装性和隐藏性,但这样也使得我们的操作更加灵活多变
友元类的使用
#include<iostream>
#include<cmath> //后面有用到平方根函数sqrt
using namespace std;
class Point
{
public:
Point();
Point(double x,double y);
private:
double x_axis;
double y_axis;
friend class Beeline; //声明Beeline类是Point类的友元类。
};
class Beeline
{
public:
Beeline();
Beeline(Point aa,Point bb);
Beeline(double a_x,double a_y,double b_x,double b_y);
double length();
private:
Point a;
Point b;
};
Point::Point():x_axis(0),y_axis(0) { }
Point::Point(double x,double y):x_axis(x),y_axis(y) { }
Beeline::Beeline()
{
a.x_axis=0; //对于友元类Beeline,其所有的成员函数都可以
a.y_axis=0; //访问Point类中的所有成员
b.x_axis=0;
b.y_axis=0;
}
Beeline::Beeline(Point aa,Point bb):a(aa),b(bb) { }
Beeline::Beeline(double a_x,double a_y,double b_x,double b_y)
{
a.x_axis=a_x;
a.y_axis=a_y;
b.x_axis=b_x;
b.y_axis=b_y;
}
double Beeline::length()
{
double length=0;
length=sqrt((a.x_axis-b.x_axis)*(a.x_axis-b.x_axis)
+(a.y_axis-b.y_axis)*(a.y_axis-b.y_axis));
return length;
}
int main()
{
Beeline line(0,0,3,4);
cout<<"线段的长度是:"<<line.length()<<endl;
return 0;
}
const修饰符
- 在定义变量时如果加上了关键字const则变量的值在程序运行期间不能改变,这种变量称为常变量
- 系统会为常变量分配空间,但是常变量必须进行初始化
- const 也可以修饰引用,限定不能通过该引用来修改值
- const修饰对象,对象的所有数据成员在对象的生存期是不可以更改的
- 常对象也必须进行初始化
- 常对象不能调用除常成员函数之外的普通成员函数
运算符重载
- 可以通过成员函数实现运算符重载,也可以通过友元函数实现运算符重载
继承
- 继承的好处:可以减少重复的代码
- 继承的语法:class 子类:继承方式 父类
- 公有继承
- 保护继承:父类中的公有权限和保护权限在子类中都是保护权限
- 私有继承:父类中均变为私有权限
- 父类中非静态成员属性都会被子类继承下去,父类中私有成员属性被编译器隐藏了,所以访问不到
- 子类继承父类后,当创建子类对象时也会调用父类的构造函数
- 继承中先调用父类的构造函数再调用子类的构造函数,析构顺序与构造相反
- 子类对象与父类出现同名成员时,直接访问的是子类的成员,若想访问父类成员只需要加个作用域即可
class Base1
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son1 :public Base1
{
public :
void func()
{
m_A = 10;
m_B = 10;
//m_C = 10;
}
};
void test01()
{
Son1 s1;
s1.m_A = 100;
//s1.m_B = 100;//Son1中m_B是保护权限,类外访问不到
}
class Son2:protected Base1
{
public:
void func()
{
m_A = 100;
m_B = 100;
//m_C = 100;
}
};
void test02()
{
Son2 s2;
//s2.m_A = 1000;//m_A变成了保护权限,类外访问不到
}
class Son3 :private Base1
{
public:
void func()
{
m_A = 1000;
m_B = 1000;
//m_C = 1000;//访问不到
}
};
class GrandSon3 :public Son3
{
public:
void func()
{
//m_A = 100;//到了Son3中m_A变为私有,所以即使是公有继承也访问不到
}
};
void test03()
{
Son2 s3;
//s3.m_A = 1000;//m_A变成了保护权限,类外访问不到
}
菱形继承导致出现二义性,在一个类中保留多份同名成员占用较多的操作空间,因此引入了虚基类方法,使得派生类在继承共同基类时只保留一份同名成员
实际上在继承共同基类时继承的是同名成员的指针
class 派生类名 :virtual 继承方式 基类名
多态
虚函数,将基类的成员函数声明为虚函数,用virtual关键字,可以防止无法根据对象地址找到正确派生类的成员函数,原因就是绑定太早完成,就是希望在执行的时候才进行绑定,这时候就需要把基类中的某些成员函数声明为虚函数。
在派生类中定义与基类同名的的成员函数称为重新定义,如果重新定义的是虚函数就称为超载,有的教科书上称为覆盖
类和对象的大小是其中所有数据成员的大小的总和
如果基类中有虚函数,编译器会自动为每一个由该基类及其派生类所定义的对象加上一个叫v-pointer的指针,简称VPTR
编译器还为每个类加上了一个叫v-table的表,vptr指向v-table的开头
纯虚函数与抽象类 一般的基类有完整的成员函数,因此可以使用他来声明新的对象,有时候我们需要避免基类创建对象
为了阻止基类来定义对象可以将virtual改为pure virtual ,含有纯虚函数的类称为抽象类,如果类中的所有虚函数都是纯虚函数,则称此基类为纯抽象类
不论是抽象类还是纯抽象类,都无法用来定义实例
持续更新中