为了保证内容完整性,将day4的类搬运一下。
感谢大佬几款优秀的支持C、C++在线编译器
stage1——6天入门阶段
教程网站:C++ 教程
在线编译器:compile c++ gcc online
刷题网站:阶段1第一关:基本数据类型
day6 planA
教程2(1),刷题3(3)
教程完成度100%,刷题完成度66.6%
主要原因:其实今天有点怠惰了…基础知识影响速度
Q&A
1.类&对象
类是 C++ 的核心特性,通常被称为用户定义的类型。
类用于指定对象的形式,它包含了数据表示法和用于处理数据的方法。类中的数据、方法、函数称为类的成员。
1.1 类的定义
它定义了类的对象包括了什么,以及可以在这个对象上执行哪些操作。
注意:class
和struct
定义时,{}
后一定要加;
class cloth
{
public:
int num;
char color[10];
};
int main()
{
cloth c;
return 0;
}
在类对象作用域内,public
公共成员在类的外部是可访问的。
对象是类的实例,对类进行定义后就获得了一个对象。
#include <iostream>
using namespace std;
class cloth
{
public:
int num;
int types;
char brand[20];
int sum()//可以直接在class里面定义,也可以在外面定义,但要在主函数前面
{
return num*types;
}
void get(int n, int t);
};
void cloth::get(int n, int t)
{
num=n;
types=t;
}
int main()
{
cloth c;
//c.num=5;
//c.types=2;
c.get(5,2);
int output=c.sum();
cout << "the sum of cloth is " << output << endl;
return 0;
}
梳理一下写的时候会遗漏的东西:
1)class
中要注意写上public:
或private:
或protected:
2)如果class
中的函数成员要在类之外定义,那么一定要加上范围解析运算符,比如cloth::
3)给class
中成员赋值或者调用函数的时候,记得使用类名.
,如c.
这样的格式去调用成员
但是私有的成员和受保护的成员不能使用直接成员访问运算符.
来直接访问
1.2 类成员函数
类的成员函数,就是指那些把定义和原型写在类定义内部的函数,就像类定义中的其他变量一样,是类的一个成员,它可以操作类的任意对象,可以访问对象中的所有成员。
成员函数可以定义在类定义内部,或者单独使用范围解析运算符::
来定义,在::
运算符之前必须使用类名。
#include <iostream>
#include <cmath>
using namespace std;
class box
{
public:
int a;
int b;
int get(int c);
void put(int d)
{
cout << d << endl;
}
};
int box::get(int c)
{
return pow(c,3)+a+b;
}
int main()
{
box bbox;
bbox.a=1;
bbox.b=2;
int output=bbox.get(3);
bbox.put(output);
return 0;
}
1.3 类访问修饰符
数据封装是面向对象编程的一个重要特点,它防止函数直接访问类类型的内部成员。类成员的访问限制
是通过在类主体内部对各个区域标记public
、private
、protected
来指定的。关键字public
、private
、protected
称为访问修饰符。
成员和类的默认访问修饰符是private
每种修饰符后可以定义多个成员,不需要{}
,直接指令就可以。
public
公有成员在程序中类的外部是可访问的
private
private
变量或函数在类的外部是不可访问的,甚至是不可查看的。只有类和友元函数
可以访问私有成员
#include <iostream>
#include <cmath>
using namespace std;
class box
{
private:
int p;
public:
int a;
int b;
int get(int c);
void put(int d)
{
cout << d << endl;
cout << p << endl;
}
void set(int s)
{
p=s;
}
};
int box::get(int c)
{
return pow(c,3)+a+b;
}
int main()
{
box bbox;
bbox.a=1;
bbox.b=2;
int output=bbox.get(3);
bbox.set(4);//就是只能通过在类中定义函数对private成员进行定义,不能直接用bbox.p=4;这样的指令
bbox.put(output);
return 0;
}
友元函数具体介绍看1.8
protected
protected
成员在类、友元函数和派生类(即子类)中是可访问的。
#include <iostream>
using namespace std;
#if 1
class box
{
protected:
int a;
};
class pbox:box//定义派生类
{
public:
void set(int b)
{
a=b;
}
int change();
};
int pbox::change()
{
return a+1;
}
int main()
{
pbox bbox;
bbox.set(5);
int output=bbox.change();
cout << output << endl;
return 0;
}
#endif
1.4 继承中的特点
派生类继承基类中的成员之后,由于继承方式的不同,会相应改变成员所属的性质:
1.public 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:public, protected, private
2.protected 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:protected, protected, private
3.private 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:private, private, private
整理一下,大概就是:public < protected < private的限制性,因此public继承不改变任何属性,protected继承将public变为protected,private继承将所有类型变为private
但无论哪种继承方式,上面两点都没有改变:
1.private
成员只能被本类成员(类内)和友元访问,不能被派生类访问;
2.protected
成员可以被派生类访问。
public
继承
#include <iostream>
using namespace std;
class box
{
public:
int a;
box()
{
a=1;
b=2;
c=3;
}
void output();
protected:
int b;
private:
int c;
};
void box::output()
{
cout << a << " " << b << " " << c << endl;
}
class pbox: public box//定义派生类
{
public:
int d;
pbox(int i)
{
box();
d=i;
}
void output()
{
cout << a << " " << b << " " << d << endl;
}
};
int main()
{
pbox p(4);
p.output();
return 0;
}
protected
继承
注意下面在B
中继承A
里的a1
会从public
变为protected
,所以在派生类外不可访问了。
#include<iostream>
#include<assert.h>
using namespace std;
class A{
public:
int a;
A(){
a1 = 1;
a2 = 2;
a3 = 3;
a = 4;
}
void fun(){
cout << a << endl; //正确
cout << a1 << endl; //正确
cout << a2 << endl; //正确
cout << a3 << endl; //正确
}
public:
int a1;
protected:
int a2;
private:
int a3;
};
class B : protected A{
public:
int a;
B(int i){
A();
a = i;
}
void fun(){
cout << a << endl; //正确,public成员。
cout << a1 << endl; //正确,基类的public成员,在派生类中变成了protected,可以被派生类访问。
cout << a2 << endl; //正确,基类的protected成员,在派生类中还是protected,可以被派生类访问。
cout << a3 << endl; //错误,基类的private成员不能被派生类访问。
}
};
int main(){
B b(10);
cout << b.a << endl; //正确。public成员
cout << b.a1 << endl; //错误,protected成员不能在类外访问。
cout << b.a2 << endl; //错误,protected成员不能在类外访问。
cout << b.a3 << endl; //错误,private成员不能在类外访问。
system("pause");
return 0;
}
private
继承
注意下面在B
中继承A
里的a1
会从public
变为private
,a2
从protected
变为private
,所以不可再被派生访问了,但在继承的派生类中可以访问,也不能在类外访问。
#include<iostream>
#include<assert.h>
using namespace std;
class A{
public:
int a;
A(){
a1 = 1;
a2 = 2;
a3 = 3;
a = 4;
}
void fun(){
cout << a << endl; //正确
cout << a1 << endl; //正确
cout << a2 << endl; //正确
cout << a3 << endl; //正确
}
public:
int a1;
protected:
int a2;
private:
int a3;
};
class B : private A{
public:
int a;
B(int i){
A();
a = i;
}
void fun(){
cout << a << endl; //正确,public成员。
cout << a1 << endl; //正确,基类public成员,在派生类中变成了private,可以被派生类访问。
cout << a2 << endl; //正确,基类的protected成员,在派生类中变成了private,可以被派生类访问。
cout << a3 << endl; //错误,基类的private成员不能被派生类访问。
}
};
int main(){
B b(10);
cout << b.a << endl; //正确。public成员
cout << b.a1 << endl; //错误,private成员不能在类外访问。
cout << b.a2 << endl; //错误, private成员不能在类外访问。
cout << b.a3 << endl; //错误,private成员不能在类外访问。
system("pause");
return 0;
}
1.5 类构造函数
类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。
构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。
就像上面程序里的A(){}
、B(int a){}
这样。
#include <iostream>
using namespace std;
class box
{
public:
box(int i)
{
cout << "the program's starting." << endl;
a=i;
}
void set()
{
cout << "a=" << a << endl;
}
private:
int a;
};
int main()
{
box b(6);
b.set();
return 0;
}
输出-----------------
the program's starting.
a=6
也可以设置什么参数都不用加的构造函数:
#include <iostream>
using namespace std;
class Line
{
public:
Line(); // 这是构造函数
};
// 成员函数定义,包括构造函数
Line::Line(void)
{
cout << "Object is being created" << endl;
}
// 程序的主函数
int main( )
{
Line line;
return 0;
}
使用初始化列表来初始化字段:
Line::Line( double len): length(len)
{
cout << "Object is being created, length = " << len << endl;
}
//等于
Line::Line(double len)
{
length=len;
cout << "Object is being created, length = " << len << endl;
}
多个字段初始化:
Line::Line( double len,double wid, double hei): length(len),width(wid),height(hei)
{
cout << "Object is being created, length = " << len << endl;
}
1.6 析构函数
类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。
析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号~
作为前缀,它不会返回任何值,也不能带有任何参数。
析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
#include <iostream>
#include <iostream>
using namespace std;
class Line
{
public:
Line(); // 这是构造函数
~Line(); //析构函数
};
// 成员函数定义,包括构造函数
Line::Line(void)
{
cout << "Object is being created." << endl;
}
Line::~Line()
{
cout << "Object is over." << endl;
}
// 程序的主函数
int main( )
{
Line line;
int a=1,b=2;
cout << a+b << endl;
return 0;
}
输出-------------
Object is being created.
3
Object is over.
1.7 拷贝构造函数
拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。
- 通过使用另一个同类型的对象来初始化新创建的对象。
- 复制对象把它作为参数传递给函数。
- 复制对象,并从函数返回这个对象。
如果在类中没有定义拷贝构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。
classname (const classname &obj) {
// 构造函数的主体
}
参考调用拷贝构造函数的三种情况、拷贝构造函数在哪几种情况下会被调用。
1.直接用另一个类的对象初始化
eg.
Line line;
Line cp(line);
Line cy=line;
2.函数形参是类的对象(必须是值传递,比如下面的例子 void display(Line obj),引用传递不会)
3.函数返回值是类的对象或引用
eg.
Line get(int a){}
Line &get(int a){}
#include <iostream>
using namespace std;
class Line
{
public:
int getLength( void );
Line( int len ); // 简单的构造函数
Line( const Line &obj); // 拷贝构造函数
~Line(); // 析构函数
private:
int *ptr;
};
// 成员函数定义,包括构造函数
Line::Line(int len)
{
cout << "调用构造函数" << endl;
// 为指针分配内存
ptr = new int;
*ptr = len;
}
Line::Line(const Line &obj)
{
cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl;
ptr = new int;
*ptr = *obj.ptr; // 拷贝值
}
Line::~Line(void)
{
cout << "释放内存" << endl;
delete ptr;
}
int Line::getLength( void )
{
return *ptr;
}
void display(Line obj)
{
cout << "line 大小 : " << obj.getLength() <<endl;
}
// 程序的主函数
int main( )
{
Line line(10);
display(line);
Line l(line);//或者Line l=line;
display(l);
return 0;
}
输出------------------------
调用构造函数
调用拷贝构造函数并为指针 ptr 分配内存
line 大小 : 10
释放内存
调用拷贝构造函数并为指针 ptr 分配内存
调用拷贝构造函数并为指针 ptr 分配内存
line 大小 : 10
释放内存
释放内存
释放内存
ps. 疑问:问什么明明在主函数中没有命令使用拷贝构造函数时,也会调用拷贝构造函数呢???
1.8 友元函数
类的友元函数
是定义在类外部,但有权访问类的所有私有private
成员和保护protected
成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。
友元可以是一个函数,该函数被称为友元函数
;友元也可以是一个类,该类被称为友元类
,在这种情况下,整个类及其所有成员都是友元。
如果要声明函数为一个类的友元,需要在类定义中该函数原型前使用关键字 friend
。
class Box
{
double width;
public:
double length;
friend void printWidth( Box box );//正常声明函数,然后前面加上friend
friend class boxer;//将boxer的所有成员函数作为box的友元
void setWidth( double wid );
};
友元函数其实可以看做一个独立的子函数,只是可以访问类中的成员变量,需要在类中声明(用friend
)。
注意:
1)在类中声明,用friend
;
2)在class
外部定义,不需要加类名和::
符号;
3)可以访问class
中任何成员,函数形参为类名+类形参
;
4)访问类成员的时候和主函数一样,要用类名.
;
5)调用的时候不需要加类名.
。
#include <iostream>
using namespace std;
class box
{
public:
int a;
friend void put(box b);
};
void put(box b)
{
cout << b.a << endl;
}
int main()
{
box bbox;
bbox.a=1;
put(bbox);
return 0;
}
1.9 内联函数
C++ 内联函数是通常与类一起使用。如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方。
对内联函数进行任何修改,都需要重新编译函数的所有客户端,因为编译器需要重新更换一次所有的代码,否则将会继续使用旧的函数。
在函数名前面放置关键字inline
,如果已定义的函数多于一行,编译器会忽略inline
限定符。
在类定义中的定义的函数都是内联函数,即使没有使用inline
说明符。
需要注意2点:
1)在内联的函数定义前加上inline
,比如inline void get() {cout << "hello world!" << endl;}
2)一般只有一句指令,否则编译器忽视掉inline
1.10 this
指针
在指针的部分提及过,主要是使用lambda
函数[]()->type{}
的时候,如果用了this
指针,就要显式传入。
在 C++ 中,每一个对象都能通过this
指针来访问自己的地址。this
指针是所有成员函数的隐含参数(友元函数没有)。因此,在成员函数内部,它可以用来指向调用对象。
#include <iostream>
#include <cmath>
using namespace std;
class Line
{
public:
Line(int b) // 简单的构造函数
{
a=b;
}
int con()
{
return pow(a,2);
}
int cmp(Line L)
{
return this->con() > L.con();
}
private:
int a;
};
// 程序的主函数
int main( )
{
Line l1(3);
Line l2(5);
if(l1.cmp(l2))
cout << "l1 > l2." << endl;
else
cout << "l1 < l2." << endl;
return 0;
}
注意:
1)在类的函数中使用this
指针,通常是要调用另一个类中的成员,因此函数形参为类
2)使用this
要和->
相结合来调用本类里成员,指向当前类的某个成员
1.11 指向类的指针
和指向结构体的指针一样。
注意:
1)使用之前需要定义,比如class box{}; int main(){ box *b; return 0;}
2)赋值要结合&
符号,比如box c; b=&c;
2)要用->
访问成员,比如b->num;
//搬运
#include <iostream>
using namespace std;
class Box
{
public:
// 构造函数定义
Box(double l=2.0, double b=2.0, double h=2.0)
{
cout <<"Constructor called." << endl;
length = l;
breadth = b;
height = h;
}
double Volume()
{
return length * breadth * height;
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
int main(void)
{
Box Box1(3.3, 1.2, 1.5); // Declare box1
Box Box2(8.5, 6.0, 2.0); // Declare box2
Box *ptrBox; // Declare pointer to a class.
// 保存第一个对象的地址
ptrBox = &Box1;
// 现在尝试使用成员访问运算符来访问成员
cout << "Volume of Box1: " << ptrBox->Volume() << endl;
// 保存第二个对象的地址
ptrBox = &Box2;
// 现在尝试使用成员访问运算符来访问成员
cout << "Volume of Box2: " << ptrBox->Volume() << endl;
return 0;
}
1.12 类的静态成员
使用static
关键字来把类成员定义为静态的。当我们声明类的成员为静态时,这意味着无论创建多少个类的对象,静态成员都只有一个副本。就是值是统一的。
如果不存在其他的初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为零。
注意!!:不能把静态成员的初始化放置在类的定义中,但是可以在类的外部通过使用范围解析运算符::
来重新声明静态变量从而对它进行初始化
#include <iostream>
#include <cmath>
using namespace std;
class Line
{
public:
static int s;
Line(int b) // 简单的构造函数
{
a=b;
}
int con()
{
return pow(a,s);
}
private:
int a;
};
int Line::s=5;
// 程序的主函数
int main( )
{
Line l(3);
cout << l.con() << endl;
return 0;
}
2.继承
继承允许我们依据另一个类
来定义一个类,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类
,新建的类称为派生类
。
继承代表了is a
关系。
// 基类
class Animal {
// eat() 函数
// sleep() 函数
};
//派生类
class Dog : public Animal {//这里就声明了public继承
// bark() 函数
};
使用派生类的注意事项:
1)派生类需要结合:
符号使用;
2)派生类自己的定义和基类是一样的,比如函数在类外定义要用派生类名::
3)派生类只有public
才可以调用基类的protected
,所以一定记得加上public
;
4)在派生类中可以修改基类的protected
成员的值。
继承时不显示声明时,则默认是private
继承。在struct
中默认public
继承。