2024年C C++最新C++类和对象详解,2024年最新大厂C C++高级多套面试专题整理集合

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

class Student{

private:

char *m_name;

int m_age;

float m_score;

public:

//声明构造函数

Student(char *name, int age, float score);

//声明普通成员函数

void show();

};

//定义构造函数

Student::Student(char *name, int age, float score){

m_name = name;

m_age = age;

m_score = score;

}

//定义普通成员函数

void Student::show(){

cout<<m_name<<“的年龄是”<<m_age<<“,成绩是”<<m_score<<endl;

}

int main(){

//创建对象时向构造函数传参

Student stu(“小明”, 15, 92.5f);

stu.show();

//创建对象时向构造函数传参

Student *pstu = new Student(“李华”, 16, 96);

pstu -> show();

return 0;

}

该例在 Student 类中定义了一个构造函数Student(char *, int, float),它的作用是给三个 private 属性的成员变量赋值。要想调用该构造函数,就得在创建对象的同时传递实参,并且实参由( )包围,和普通的函数调用非常类似。

在栈上创建对象时,实参位于对象名后面,例如Student stu("小明", 15, 92.5f);在堆上创建对象时,实参位于类名后面,例如new Student("李华", 16, 96)

构造函数必须是 public 属性的,否则创建对象时无法调用。当然,设置为 private、protected 属性也不会报错,但是没有意义。

构造函数没有返回值,因为没有变量来接收返回值,即使有也毫无用处,这意味着:

  • 不管是声明还是定义,函数名前面都不能出现返回值类型,即使是 void 也不允许;

  • 函数体中不能有 return 语句。

构造函数的重载

=======

和普通成员函数一样,构造函数是允许重载的。一个类可以有多个重载的构造函数,创建对象时根据传递的实参来判断调用哪一个构造函数。

构造函数的调用是强制性的,一旦在类中定义了构造函数,那么创建对象时就一定要调用,不调用是错误的。如果有多个重载的构造函数,那么创建对象时提供的实参必须和其中的一个构造函数匹配;反过来说,创建对象时只有一个构造函数会被调用。

默认构造函数

======

如果用户自己没有定义构造函数,那么编译器会自动生成一个默认的构造函数,只是这个构造函数的函数体是空的,也没有形参,也不执行任何操作。比如上面的 Student 类,默认生成的构造函数如下:

Student(){}

一个类必须有构造函数,要么用户自己定义,要么编译器自动生成。

一旦用户自己定义了构造函数,不管有几个,也不管形参如何,编译器都不再自动生成。

实际上编译器只有在必要的时候才会生成默认构造函数,而且它的函数体一般不为空。默认构造函数的目的是帮助编译器做初始化工作,而不是帮助程序员。这是C++的内部实现机制,这里不再深究,初学者可以按照上面说的“一定有一个空函数体的默认构造函数”来理解。

最后需要注意的一点是,调用没有参数的构造函数也可以省略括号。对于示例的代码,在栈上创建对象可以写作Student stu()Student stu,在堆上创建对象可以写作Student *pstu = new Student()Student *pstu = new Student,它们都会调用构造函数 Student()。

以前我们就是这样做的,创建对象时都没有写括号,其实是调用了默认的构造函数。

初始化列表

=====

构造函数的一项重要功能是对成员变量进行初始化,可以在构造函数的函数体中对成员变量一一赋值,还可以采用初始化列表。

#include

using namespace std;

class Student{

private:

char *m_name;

int m_age;

float m_score;

public:

Student(char *name, int age, float score);

void show();

};

//采用初始化列表

Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){

//TODO:

}

如本例所示,定义构造函数时并没有在函数体中对成员变量一一赋值,其函数体为空(当然也可以有其他语句),而是在函数首部与函数体之间添加了一个冒号:,后面紧跟m_name(name), m_age(age), m_score(score)语句,这个语句的意思相当于函数体内部的m_name = name; m_age = age; m_score = score;语句,也是赋值的意思。

使用构造函数初始化列表并没有效率上的优势,仅仅是书写方便,尤其是成员变量较多时,这种写法非常简单明了。

注意,成员变量的初始化顺序与初始化列表中列出的变量的顺序无关,它只与成员变量在类中声明的顺序有关。

创建对象时系统会自动调用构造函数进行初始化工作,同样,销毁对象时系统也会自动调用一个函数来进行清理工作,例如释放分配的内存、关闭打开的文件等,这个函数就是析构函数。

析构函数

=======

析构函数(Destructor)也是一种特殊的成员函数,没有返回值,不需要程序员显式调用(程序员也没法显式调用),而是在销毁对象时自动执行。构造函数的名字和类名相同,而析构函数的名字是在类名前面加一个~符号。

注意:析构函数没有参数,不能被重载,因此一个类只能有一个析构函数。如果用户没有定义,编译器会自动生成一个默认的析构函数。

#include

using namespace std;

class VLA{

public:

VLA(int len); //构造函数

~VLA(); //析构函数

public:

void input(); //从控制台输入数组元素

void show(); //显示数组元素

private:

int *at(int i); //获取第i个元素的指针

private:

const int m_len; //数组长度

int *m_arr; //数组指针

int *m_p; //指向数组第i个元素的指针

};

VLA::VLA(int len): m_len(len){ //使用初始化列表来给 m_len 赋值

if(len > 0){ m_arr = new int[len]; /分配内存/ }

else{ m_arr = NULL; }

}

VLA::~VLA(){

delete[] m_arr; //释放内存

}

void VLA::input(){

for(int i=0; m_p=at(i); i++){ cin>>*at(i); }

}

void VLA::show(){

for(int i=0; m_p=at(i); i++){

if(i == m_len - 1){ cout<<*at(i)<<endl; }

else{ cout<<*at(i)<<", "; }

}

}

int * VLA::at(int i){

if(!m_arr || i<0 || i>=m_len){ return NULL; }

else{ return m_arr + i; }

}

int main(){

//创建一个有n个元素的数组(对象)

int n;

cout<<"Input array length: ";

cin>>n;

VLA *parr = new VLA(n);

//输入数组元素

cout<<"Input “<<n<<” numbers: ";

parr -> input();

//输出数组元素

cout<<"Elements: ";

parr -> show();

//删除数组(对象)

delete parr;

return 0;

}

析构函数的执行时机

=========

析构函数在对象被销毁时调用,而对象的销毁时机与它所在的内存区域有关。

在所有函数之外创建的对象是全局对象,它和全局变量类似,位于内存分区中的全局数据区,程序在结束执行时会调用这些对象的析构函数。

在函数内部创建的对象是局部对象,它和局部变量类似,位于栈区,函数执行结束时会调用这些对象的析构函数。

new 创建的对象位于堆区,通过 delete 删除时才会调用析构函数;如果没有 delete,析构函数就不会被执行。

#include

#include

using namespace std;

class Demo{

public:

Demo(string s);

~Demo();

private:

string m_s;

};

Demo::Demo(string s): m_s(s){ }

Demo::~Demo(){ cout<<m_s<<endl; }

void func(){

//局部对象

Demo obj1(“1”);

}

//全局对象

Demo obj2(“2”);

int main(){

//局部对象

Demo obj3(“3”);

//new创建的对象

Demo *pobj4 = new Demo(“4”);

func();

cout<<“main”<<endl;

return 0;

}

this 是 一个关键字,也是一个 const指针,它指向当前对象,通过它可以访问当前对象的所有成员。

所谓当前对象,是指正在使用的对象。例如对于stu.show();,stu 就是当前对象,this 就指向 stu。

注意,this 是一个指针,要用->来访问成员变量或成员函数。

this 虽然用在类的内部,但是只有在对象被创建以后才会给 this 赋值,并且这个赋值的过程是编译器自动完成的,不需要用户干预,用户也不能显式地给 this 赋值。

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

注意,this 是一个指针,要用->来访问成员变量或成员函数。

this 虽然用在类的内部,但是只有在对象被创建以后才会给 this 赋值,并且这个赋值的过程是编译器自动完成的,不需要用户干预,用户也不能显式地给 this 赋值。

[外链图片转存中…(img-cFbBZcAo-1715547582038)]
[外链图片转存中…(img-HeHzCkUA-1715547582038)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 26
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值