1.概念:
类的定义:在C++中, 用 "类" 来描述 "对象", 所谓的"对象"是指现实世界中的一切事物。那么类就可以看做是对相似事物的抽象, 找到这些不同事物间的共同点, 如自行车和摩托车, 首先他们都属于"对象", 并且具有一定得相同点, 和一些不同点, 相同点如他们都有质量、都有两个轮子, 都是属于交通工具等。"都有质量"、"两个轮子"属于这个对象的属性, 而"都能够当做交通工具"属于该对象具有的行为, 也称方法。
类的一些知识点:
类的使用:class a;class b。
std是一个类,包括了cout,cin等成员函数,c++标准程序库中所有的标识符都被定义于一个名为std的namespace。namespace:命名空间,很多函数或类都是置于命名空间里以供使用,但是不同命名空间内的类不一定相同。例如:std::string, AAA::string,这两者不一定相同。
类中含有类成员函数,成员。类成员函数的实现:void Stock::update();意思是声明一个无返回值的一个类成员函数update,该函数属于Stock这个类。
public,private(protected后续讨论):public中的成员类外可以使用,而private中的成员仅有类中可以使用;public 与 private 为属性/方法限制的关键字, private 表示该部分内容是私密的, 不能被外部所访问或调用, 只能被本类内部访问; 而 public 表示公开的属性和方法, 外界可以直接访问或者调用。一般来说类的属性成员都应设置为private, public只留给那些被外界用来调用的函数接口, 但这并非是强制规定, 可以根据需要进行调整;
struct和class的区别
在c++中struct和class的唯一区别就在于默认的访问权限不同
区别:
struct默认权限为公共;
class默认权限为私有;
构造函数与析构函数:类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。 构造函数可用于为某些成员变量设置初始值。构造函数含有默认构造函数,含参构造函数,拷贝构造函数。析构函数是一个成员函数,在对象超出范围或通过调用 delete 或 delete[] 显式销毁对象时,会自动调用析构函数。 析构函数与类同名,前面带有波形符 (~)。 例如,声明 String 类的析构函数:~String()。
如果你未定义析构函数,编译器会提供一个默认的析构函数;对于某些类来说,这就足够了。 当类维护必须显式释放的资源(例如系统资源的句柄,或指向在类的实例被销毁时应释放的内存的指针)时,你需要定义一个自定义的析构函数。
例子:
class String
{
public:
String(const char* ch); // Declare the constructor
~String(); // Declare the destructor
private:
char* _text{nullptr};
};
默认构造函数形式:
class Date
{
public:
Date()
{
year = 1;
month = 1;
day = 1;
}
}
含参构造函数:
class Date
{
public:
Date(int y, int m, int d)
{
day = d;
month = m;
year = y;
}
}
拷贝构造函数:
浅拷贝:首先先观察浅拷贝的结果(如下程序),从输出结果可以看出,a2是对a1进行的浅拷贝,只是拷贝了数组arr指针的本身,因为当对a1数组进行修改时,a2的数组也受到了影响,因为两个所指向的内存空间一致。
class A {
public:
int *arr;
A(int n) {//构造函数
arr = new int[n];
}
A(const A& a) {//复制构造函数
arr = a.arr;
}
};
int main() {
A a1(1);
a1.arr[0] = 1;
printf("对象a1的数据:%d\n", a1.arr[0]);
A a2 = a1;
printf("对象a2的数据:%d\n", a2.arr[0]);
a1.arr[0] = 2;//对象a1数据修改为2
printf("仅对象a1数据修改后:\n");
printf("对象a1的数据:%d\n", a1.arr[0]);
printf("对象a2的数据:%d\n", a2.arr[0]);
}
/*输出
对象a1的数据:1
对象a2的数据:1
仅对象a1数据修改后:
对象a1的数据:2
对象a2的数据:2
*/
深拷贝:接下来,举例深拷贝,修改一下上面代码为,从输出结果可以看出,深拷贝实现了两个对象的数组分别占用不同的内存空间,因为两个不会互相影响。
class A {
public:
int *arr;
A(int n) {//构造函数
arr = new int[n];
}
A(const A& a) {//复制构造函数
int n = sizeof(a.arr) / 4;
arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = a.arr[i];
}
}
};
int main() {
A a1(1);
a1.arr[0] = 1;
printf("对象a1的数据:%d\n", a1.arr[0]);
A a2 = a1;
printf("对象a2的数据:%d\n", a2.arr[0]);
a1.arr[0] = 2;//对象a1数据修改为2
printf("仅对象a1数据修改后:\n");
printf("对象a1的数据:%d\n", a1.arr[0]);
printf("对象a2的数据:%d\n", a2.arr[0]);
}
/*输出:
对象a1的数据:1
对象a2的数据:1
仅对象a1数据修改后:
对象a1的数据:2
对象a2的数据:1(update)
*/
由上述可以引出一个新的概念(new):
C++ 程序中的内存分为两个部分:
- 栈:在函数内部声明的所有变量都将占用栈内存。
- 堆:这是程序中未使用的内存,在程序运行时可用于动态分配内存。
很多时候,您无法提前预知需要多少内存来存储某个定义变量中的特定信息,所需内存的大小需要在运行时才能确定。
在 C++ 中,您可以使用特殊的运算符为给定类型的变量在运行时分配堆内的内存,这会返回所分配的空间地址。这种运算符即 new 运算符。
如果您不再需要动态分配的内存空间,可以使用 delete 运算符,删除之前由 new 运算符分配的内存。
class MyClass {
public:
int data;
MyClass(int value) {
data = value;
}
};
int main() {
// 创建 MyClass 对象并初始化
MyClass* objPtr = new MyClass(42);
// 使用对象
std::cout << "Data: " << objPtr->data << std::endl;
// 释放内存
delete objPtr;
return 0;
}
如果new的对象是一个数组,那么delete写法为delete【】数组名:new arr[n];delete[]arr;
在上述示例中,通过 new MyClass(42) 使用 new 运算符创建了一个 MyClass 对象,并将值 42 传递给构造函数进行初始化。然后,可以使用对象指针 objPtr 访问对象的成员变量 data。
函数重载:
在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。您不能仅通过返回类型的不同来重载函数。
下面的实例中,同名函数 print() 被用于输出不同的数据类型:
#include <iostream>
using namespace std;
class printData
{
public:
void print(int i) {
cout << "整数为: " << i << endl;
}
void print(double f) {
cout << "浮点数为: " << f << endl;
}
void print(char c[]) {
cout << "字符串为: " << c << endl;
}
};
int main(void)
{
printData pd;
// 输出整数
pd.print(5);
// 输出浮点数
pd.print(500.263);
// 输出字符串
char c[] = "Hello C++";
pd.print(c);
return 0;
}
this指针:
在 C++ 中,this 指针是一个特殊的指针,它指向当前对象的实例。
在 C++ 中,每一个对象都能通过 this 指针来访问自己的地址。
this是一个隐藏的指针,可以在类的成员函数中使用,它可以用来指向调用对象(谁调用这个函数就指向谁)。
当一个对象的成员函数被调用时,编译器会隐式地传递该对象的地址作为 this 指针。
友元函数没有 this 指针,因为友元不是类的成员,只有成员函数才有 this 指针。
下面的实例有助于更好地理解 this 指针的概念:
#include<iostream>
using namespace std;
class Person {
public:
Person(int age){
//1、当形参和成员变量同名时,可用this指针来区分
this->age = age;//(this->age)的意思是调用这个含参构造Person函数的对象p1的age;
}
Person& PersonAddPerson(Person p){
this->age += p.age;//第一次相加时this->age表示p2的age;
//返回对象本身
return *this;//返回相加后的p2;
}
int age;
};
int main() {
Person p1(10);
cout << "p1.age = " << p1.age << endl;
Person p2(20);
p2.PersonAddPerson(p1).PersonAddPerson(p1); //20+10+10=40
cout << "p2.age = " << p2.age << endl;
return 0;
}
另外:this指针本质是指针常量,指针的指向是不可以修改的。如下:
class Person {
public:
void showPerson()
{
m_A = 100;
this = NULL;//此处会报错,因为this是指针常量,不会改变他的指向,即不会改变p的值;
}
int m_A;
};
int main() {
Person p;
}
空指针访问成员函数:空指针可以访问无成员数值的成员函数;但不可以访问有数据的成员函数,示例如下:
class as{
public:
void showclassname() {
cout << "this class 1" << endl;
}
void showPersonage() {
cout << "age=" << m_age << endl;
}
int m_age=0;
};
int main() {
as a;
as* p = NULL;
p->showclassname();//可成功运行;
p->showPersonage();//此处会报错,因为showPerson()函数中含有成员的数值;
return 0;
}
const修饰成员函数:成员函数后加const我们称这个函数为常函数,常函数内不可以修改成员属性,成员属性声明时加mutable后,在常函数中仍可以修改。例:void Person()const{},
class Person {
public:
void showPerson() const
{
m_A = 100;//此处会报错,
b = 100;//因为变量b被muutable修饰,所以此处可以常函数可以修改b的值;
}
int m_A;
mutable int b;
};
int main() {
Person p;
}
常对象:声明对象时在前面加const,则该对象为常对象,常对象只能调用常函数。
class Person {
public:
void showPerson() const
{
//m_A = 100;//此处会报错,
m_b = 100;//因为变量b被muutable修饰,所以此处可以常函数可以修改b的值;
}
int m_A;
mutable int m_b;
};
int main() {
const Person p;
//p.m_A = 10;//此处会报错,p是常对象,他的值不可以修改
p.m_b = 10;//特殊变量m_b,在常对象下也可以修改;
}
友元:关键词:friend;
三种友元的实现:1.全局函数做友元;2.类做友元;3.成员函数做友元。
友元的目的 就是让一个函数或者类 访问另一个类中的私有成员。
1.全局函数做友元;
class Building
{
friend void laowang1;
// Building的构造函数,给成员变量赋初值
Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
string m_SittingRoom; // 客厅
private:
string m_BedRoom; // 卧室
};
void laoWang1(Building *building)
{
cout << "隔壁老王 全局函数 正在访问:(地址传递) " << building->m_SittingRoom << endl;
cout << "隔壁老王 全局函数 正在访问:(地址传递) " << building->m_BedRoom << endl;
}//laowang1可以访问Building中的私有成员,
2.类做友元:
#include <iostream>
#include <string>
using namespace std;
// 类作友元
class LaoWang
{
public:
LaoWang();
void visit(); //参观函数 访问Building中的属性,类外定义。
Building * building;
};
// 房屋类
class Building
{
// 告诉编译器,LaoWang类是Building类的好朋友,可以访问Building类的私有成员
friend class LaoWang;
public:
Building();//类外定义,
string m_SittingRoom; // 客厅
private:
string m_BedRoom; // 卧室
};
// 类外定义成员函数
Building::Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
LaoWang::LaoWang()
{
// 创建建筑物对象
building = new Building;
}
void LaoWang::visit()
{
cout << "隔壁老王LaoWang类正在访问:" << building->m_SittingRoom << endl;
cout << "隔壁老王LaoWang类正在访问:" << building->m_BedRoom << endl;
}
void test()
{
LaoWang lw;
lw.visit();
}
int main()
{
test();
return 0;
}
3.成员函数做友元:
#include <iostream>
#include <string>
using namespace std;
class Building;
class LaoWang
{
public:
LaoWang();
void visit1(); //让visit1()函数 可以 访问Building中的私有成员
void visit2(); //让visit2()函数 不可以 访问Building中的私有成员
Building *building;
};
class Building
{
// friend告诉编译器,LaoWang类下的visit1()函数是Building类的好朋友,可以访问Building的私有成员
friend void LaoWang::visit1();
public:
Building();
string m_SittingRoom; //客厅
private:
string m_BedRoom; //卧室
};
LaoWang::LaoWang()
{
building = new Building;
}
void LaoWang::visit1()
{
cout << "隔壁老王LaoWang类中的visit1()函数正在访问:" << building->m_SittingRoom << endl;
cout << "隔壁老王LaoWang类中的visit1()函数正在访问:" << building->m_BedRoom << endl;
}
void LaoWang::visit2()
{
cout << "隔壁老王LaoWang类中的visit2()函数正在访问:" << building->m_SittingRoom << endl;
//cout << "隔壁老王LaoWang类中的visit2()函数正在访问:" << building->m_BedRoom << endl; //错误!私有属性不可访问
}
Building::Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
void test()
{
LaoWang lw;
lw.visit1();
lw.visit2();
}
int main()
{
test();
return 0;
}
2.疑难:
1.传参时的引用&具体指的是什么?
2.声明一个函数时,返回值的类型看什么来决定?
3.new和delete的用法;
4.this指针;
3.总结:
第一周类与封装学习了许多知识点,主要学习内容是了解了许多由c++带来新的概念,c++与c,大体相同,但又有所不同,例如最简单打印函数printf与cout,c++的表达更加的简介,但不能很细致,例如c++要实现%.2f,c++的cout无法实现,还是要借助printf函数。类与封装这一内容将所有事物都可以做一个类;如何再具体化一个对象;使其具有类的共有属性,然后再实现一些对对象的操作。