C++:基本之前都深入问过豆包了,没啥新东西:类和对象、get()set()、公有私有保护、作用域解析运算符::、继承、函数重载、多态(从这开始不帖回答了,只放豆包回答链接)、虚函数
开始面向对象
菜鸟教程:C++ 类 & 对象 ~ C++ 多态
访问数据成员
精简代码
#include <iostream>
using namespace std;
class Box
{
public:
double length; // 长度
double breadth; // 宽度
double height; // 高度
// // 成员函数声明
double get(void);
void set( double len, double bre, double hei );
};
// 成员函数定义
double Box::get(void)
{
return length * breadth * height;
}
void Box::set( double len, double bre, double hei)
{
length = len;
breadth = bre;
height = hei;
}
int main()
{
Box Box1; // 声明 Box1,类型为 Box
Box Box3; // 声明 Box3,类型为 Box
double volume = 0.0; // 用于存储体积
Box1.height = 5.0;
Box1.length = 6.0;
Box1.breadth = 7.0;
volume = Box1.height * Box1.length * Box1.breadth;
cout << volume <<endl;
Box3.set(16.0, 8.0, 12.0);
volume = Box3.get();
cout << volume <<endl;
}
首先关于get()和set():
1、
Box::get()里的get不是固定的,可以叫gett等任意合法标识符,只要遵循命名规则;这里可以理解为一种虽然不是什么固定写法,可以随便起名字但为了方便导致大家都用get2、其他成员函数也可以这么弄,即类内声明、类外使用作用域解析运算符
::定义3、成员变量不能用这种方式,成员变量是在类内定义描述对象属性,而不是像成员函数那样类外通过
::定义。
成员函数定义
Box::get()虽然不在类Box里直接写函数体,但通过作用域解析运算符::表明它是类Box的成员函数,类里只是声明,函数体可在类外定义
Box1和Box3本质都是Box类的对象,只是初始化方式不同,Box1直接对成员变量赋值,Box3通过调用set成员函数来设置成员变量的值。
注意到Box类里先对get成员函数声明了, 然后外面进行了定义:
内部声明后,定义写在内外都可以
而如果内部不声明,定义只能写在内部,不可以写在外部
注意到没写private还是public:
class ExampleClass { int privateVar; // 未指定,默认 private void privateFunc() {} // 未指定,默认 private public: int publicVar; int publichaha; //未指定,由上一个public来定 };由于类没显示指定访问权限,则默认是private的,再结合没指定也由上一个指定的访问权限规则,得知上述代码
—————————————————— C++ 继承 ——————————————————
东西好多,他妈的
首先关于: 和 ::
初学者可以简单理解为
::是从左到右一次缩小范围
:则是右边的是主(基类),派生是儿子(左边)
关于访问修饰符
菜鸟教程里的class Rectangle: public Shape继承了基类的全部成员函数和成员变量
问豆包更多:
一般情况下,若继承方式为公有(
public)、保护(protected)继承,派生类会包含基类的全部成员(包括成员变量和成员函数)但对于私有(
private)继承也包含基类全部成员,只是访问权限有变化。不过,构造函数、析构函数和赋值运算符重载函数不会被继承,而是会根据自身情况重新定义。
但说了等于没说,看具体例子
#include <iostream>
class Base {
public:
int publicMember;
protected:
int protectedMember;
private:
int privateMember;
};
// 公有继承
class PublicDerived : public Base {
public:
void accessMembers() {
publicMember = 1; // 可访问
protectedMember = 2; // 可访问
// privateMember = 3; // 不可访问
}
};
// 保护继承
class ProtectedDerived : protected Base {
public:
void accessMembers() {
publicMember = 1; // 可访问
protectedMember = 2; // 可访问
// privateMember = 3; // 不可访问
}
};
// 私有继承
class PrivateDerived : private Base {
public:
void accessMembers() {
publicMember = 1; // 可访问
protectedMember = 2; // 可访问
// privateMember = 3; // 不可访问
}
};
int main() {
PublicDerived publicDerived;
publicDerived.publicMember = 1; // 可访问
ProtectedDerived protectedDerived;
// protectedDerived.publicMember = 1; // 不可访问,已变为保护成员
PrivateDerived privateDerived;
// privateDerived.publicMember = 1; // 不可访问,已变为私有成员
}
这种基本问题,没啥深度难度的问豆包就可以了
总结:
1、基类构造函数不继承,派生类一般要在构造函数里调用基类构造函数,可按需决定是否额外写逻辑;
2、公有继承可以在基类内和继承类内访问基类的公有和保护的成员变量和函数,私有成员变量和函数无法直接在派生类内和main内访问,想访问要弄个公有函数,而main里可以访问公有函数和变量,想访问保护类型变量和函数也要弄个共有函数 且公有继承,main 里能访问基类公有成员;保护和私有继承,若不写公有函数,main 里无法访问基类原公有和保护成员 。 保护继承在派生类写公有访问,然后可以在基类和派生类内访问公有和保护成员函数和变量,而main里只能访问公有成员变量和函数,对于保护和私有成员变量和函数想在main和派生类里访问都需要弄个函数 私有继承在派生类内要写个公有函数来访问基类里的公有和保护变量和函数,而访问基类里的私有变量和函数或者mian里访问都要弄个公有函数来访问公有、保护、私有成员变量和函数都要写个公有访问函数
3、
Q:
代码中派生类里为何还要写个public?然后void accessMembers?
A:
是因为外界(如
main函数)就能调用它,若不写public的accessMember函数,main里无法访问派生类非公有的、保护和私有类型成员。4、
Q:
那我看派生类里写了个public然后直接可以用基类里的相应函数和变量了,为何我看你又写了一种写法是get和set呢?
A:
写
get和set函数,是为了更好地封装和控制对成员变量的访问。直接用基类函数和变量可能会破坏封装性,而get和set能在访问时添加额外逻辑,如边界检查、数据验证等,增强代码的安全性和可维护性。
自己思考的很深,总结完得到豆包肯定后再回头看菜鸟教程里的“我们可以根据访问权限总结出不同的访问类型,如下所示”那个表就太小儿科了
自己问完豆包,再回来看多继承也明白了,感觉也没啥难的,就是太绕了,真他妈恶心
问了下豆包,找工作的东西,╮(╯▽╰)╭~~~~(>_<)~~~~ 之前的咨询
—————————————————— C++ 重载运算符和重载函数 ——————————————————
C++ 重载运算符和重载函数
重载函数
重载运算符
#include <iostream>
// 定义 Box 类
class Box {
private:
double length; // 盒子的长度
double breadth; // 盒子的宽度
double height; // 盒子的高度
public:
// 构造函数,用于初始化盒子的尺寸
Box(double l = 0, double b = 0, double h = 0) : length(l), breadth(b), height(h) {}
// 重载 + 运算符,用于把两个 Box 对象相加
Box operator+(const Box& b) {
Box box;
box.length = this->length + b.length;
box.breadth = this->breadth + b.breadth;
box.height = this->height + b.height;
return box;
}
// 成员函数,用于计算盒子的体积
double getVolume() {
return length * breadth * height;
}
};
int main() {
// 创建两个 Box 对象
Box box1(1.0, 2.0, 3.0);
Box box2(4.0, 5.0, 6.0);
// 使用重载的 + 运算符将两个 Box 对象相加
Box box3 = box1 + box2;
// 输出每个盒子的体积
std::cout << "Box 1 Volume: " << box1.getVolume() << std::endl;
std::cout << "Box 2 Volume: " << box2.getVolume() << std::endl;
std::cout << "Box 3 (box1 + box2) Volume: " << box3.getVolume() << std::endl;
}
关于未定义,我理解最终没初始化就是未定义:
关于构造函数传参问题,上一个博客,说“对于这里的构造函数,可以不写”,但实际上虽然
#include <iostream>
#include <string>
using namespace std;
struct Books{
string title;
int book_id;
// Books(string t, int id)
// : title(t), book_id(id) {}
};
void printBookInfo(const Books& book) {
cout << "书籍标题: " << book.title << endl;
cout << "书籍 ID: " << book.book_id << endl;
}
int main(){
Books Book1("C++ 教程",12345);
Book1.title="C++ 教程";
printBookInfo(Book1);
}
通过编译了,但说是可能版本还是编译器这些我最讨厌的问题,导致的
严格来说
没构造函数时,不能传参初始化对象
有带参数的构造函数,必须传参初始化对象,除非同时定义了无参数的构造函数
现在说下类成员函数
我一直因为是什么高大上的玩意,结果tmd根本不是类内函数本身有什么参数,而是main里传入的实参那,其实相当于省略了一个写法,无语
在 C++ 中,当调用类的成员函数时,编译器会自动将调用该函数的对象的地址作为一个隐含的参数传递给函数,这个隐含的参数就是
this指针。对于运算符重载函数也是如此。
当在
main函数中执行box1 + box2时,实际上等价于调用box1.operator+(box2)。这里box1的地址通过this指针隐式传递给operator+函数,而box2作为显式参数传递。
运算符重载实例
看到个有意思的
函数调用运算符 () 可以被重载用于类的对象。当重载 () 时,您不是创造了一种新的调用函数的方式,相反地,这是创建一个可以传递任意数目参数的运算符函数。
safearay()与Box(double l = 0, double b = 0, double h = 0) : length(l), breadth(b), height(h) {}都是构造函数,只是前者无参,在函数体中初始化数组,后者有默认参数,用成员初始化列表初始化成员变量。
A这个对象内部封装了一个数组。
纯傻逼,先略过
———————————————————— C++ 多态 ————————————————————
先理清楚几个知识点(都是问豆包的)
1、继承机制产生了父子类的概念,
基类叫父(被继承的类通常被叫做基类或者父类)
派生类叫子(通过继承基类产生的新类被称作派生类或者子类)
2、当我们用子类去创建一个变量时,这个变量就代表了一个子类对象。
3、多态比继成功牛逼在哪啊?
#include <iostream> // 父类 Animal class Animal { public: void call() { std::cout << "动物发出声音" << std::endl; } }; // 子类 Cat class Cat : public Animal { public: void call() { std::cout << "喵喵喵" << std::endl; } }; // 子类 Dog class Dog : public Animal { public: void call() { std::cout << "汪汪汪" << std::endl; } }; int main() { Cat cat; Dog dog; cat.call(); dog.call(); }要是修改
Cat类的call方法,由于调用是直接针对Cat对象,所有调用cat.call()的地方都要确认是否会受到影响。如果项目里有很多地方调用了cat.call(),修改时就很容易出错,维护成本高。
Q:不是吧,我想修改成“喵呜喵呜” ,直接把派生类输出换成 ”喵呜喵呜” 就好了啊,咋需要改那么多东西?
A:
普通继承场景
#include <iostream> class Animal { public: void commonFunction() { std::cout << "执行一些通用操作" << std::endl; } }; class Cat : public Animal { public: void call() { commonFunction(); std::cout << "喵喵喵" << std::endl; } }; void specificCall(Cat cat) { cat.call(); // 这里有针对 Cat 对象 call 之后的特殊操作 std::cout << "执行针对猫叫后的特殊操作" << std::endl; } int main() { Cat cat; specificCall(cat); }如果要修改
Cat类的call方法,比如添加一些额外逻辑或者修改辅助方法的调用方式,由于specificCall函数是专门针对Cat对象编写的,可能需要检查并修改specificCall函数以及其他所有调用Cat对象call方法的地方,以确保修改不会引发错误
多态场景
#include <iostream> class Animal { public: virtual void call() { std::cout << "执行一些通用操作" << std::endl; } }; class Cat : public Animal { public: void call() override { Animal::call(); std::cout << "喵喵喵" << std::endl; } }; void makeAnimalCall(Animal* animal) { animal->call(); // 这里的操作不依赖具体是哪种动物 std::cout << "执行通用的动物叫声后续操作" << std::endl; } int main() { Cat cat; makeAnimalCall(&cat); }
继续问豆包:
Q:关于你说的“牛逼四:统一处理逻辑”举例子
A:
#include <iostream> // 父类 Animal class Animal { public: virtual void call() { std::cout << "动物发出声音" << std::endl; } }; // 子类 Cat class Cat : public Animal { public: void call() override { std::cout << "喵喵喵" << std::endl; } }; // 子类 Dog class Dog : public Animal { public: void call() override { std::cout << "汪汪汪" << std::endl; } }; int main() { Animal* animals[2]; Cat cat; Dog dog; animals[0] = &cat; animals[1] = &dog; for (int i = 0; i < 2; ++i) { animals[i]->call(); } return 0; }这里可以把
Cat和Dog对象的指针存到Animal类型的数组里,然后通过循环统一调用call方法,不管有多少种动物子类,都可以用这种简洁统一的方式处理。
单纯继承情况
#include <iostream> // 父类 Animal class Animal { public: void call() { std::cout << "动物发出声音" << std::endl; } }; // 子类 Cat class Cat : public Animal { public: void call() { std::cout << "喵喵喵" << std::endl; } }; // 子类 Dog class Dog : public Animal { public: void call() { std::cout << "汪汪汪" << std::endl; } }; int main() { Cat cat; Dog dog; if (/* 判断是否是 Cat 对象 */) { cat.call(); } if (/* 判断是否是 Dog 对象 */) { dog.call(); } return 0; }
以上是我起手看菜鸟教程发现说跟继承有关系,故追问了许多
现在回头继续看菜鸟教程,直接把里面的实例1丢给豆包,又追问好久,发现懂了好多
不再记录了,太浪费时间了
不再强迫症了,以防失效,之前的 收获与成长

实例2,问完豆包后觉得太简单了,不过菜鸟教程里解释的也非常棒
以防失效

###:
妈逼的情人节0214图书馆坐位旁边一个身材一级棒,该肥的地方肥,该瘦的瘦,大奶子这tm又在磨练我心性好想一口下去
咳嗽一声太他妈销魂了,离得我还很近
可是经历使我立马冷静下来,做一个够狠够绝杀伐果断的人
女人只会磨灭我的斗志
一看是个考公的,呵呵,呵呵呵
###:学英文
class derived-class: access-specifier base-class
衍生 接近-说明符
即: 派生 访问说明符
polymorphism:多态性
###:豆包:
一句话回答我以下所有问题,且都是C++的,且每个问题换行,且如果需要写代码一律去掉最后的return 0
###:傻逼VS滚动代码跟他妈个僵尸一样,这么垃圾的残次品也这么受欢迎吗


浙公网安备 33010602011771号