尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

QT开发「1」 C++简要知识点

QT开发「1」 C++简要知识点 前言QT是基于C的跨平台开发框架所以在使用QT开发前要先有一定量的C基础其实不会问题也不大(╹ڡ╹ )这里先简单总结一些C独有的并且在QT中常用的C知识点。1.Class类C的class类里面主要包含成员变量属性成员函数方法并且还有public、private和protected的访问修饰符来修饰成员。下面的代码先写完后面再给例子看日志打印1.1 Class类创建class MyClass { public: int age() const { return my_age; } const std::string name() const { return my_name; } private: int my_age; std::string my_name; };这里定义了一个类包含私有成员变量和公共成员函数。然后在main函数中创建对象就能使用该类的成员函数。1.2初始化列表和委托构造函数class MyClass { public: // Default constructor MyClass(): MyClass(0, default){ std::cout [Constructor] age0, namedefault std::endl; } // Parameter constructor MyClass(int age, const std::string name): my_age(age), my_name(name){ std::cout [Constructor] age age , name name std::endl; } int age() const { return my_age; } const std::string name() const { return my_name; } std::string nameX() const { return my_name Hello; } void setName(const std::string name) { my_name name; } private: int my_age; std::string my_name; };初始化列表MyClass obj1(40, Stack);传入参数进行初始化确保某些成员的正确初始化。委托构造函数这里用 MyClass obj1()无参初始化时委托给MyClass(int age, const std::string name):函数进行初始化初值不会带来额外开销。1.3析构函数析构函数是C中一种特殊的成员函数用于在对象生命周期结束时自动释放资源。简单来说就是当对象结束时析构函数会执行。前缀为波浪号~无返回类型且不接受参数。class MyClass { public: // Default constructor MyClass(): MyClass(0, default){ std::cout [Constructor] age0, namedefault std::endl; } // Parameter constructor MyClass(int age, const std::string name): my_age(age), my_name(name){ std::cout [Constructor] age age , name name std::endl; } // Copy Constructor (Deep Copy) - copy data from another object // Copies member variables to create independent objects MyClass(const MyClass other) : my_age(other.my_age), my_name(other.my_name) { std::cout [CopyConstructor] Copied age my_age , name my_name std::endl; } // Move Constructor (Move Semantics) - steal resources from another object // Transfer data ownership instead of copying MyClass(MyClass other) noexcept : my_age(other.my_age), my_name(std::move(other.my_name)) { std::cout [MoveConstructor] Moved age my_age , name my_name std::endl; other.my_age 0; other.my_name ; // Clear source object } // Destructor - called when object is destroyed ~MyClass() { std::cout [Destructor] age my_age , name my_name std::endl; } int age() const { return my_age; } const std::string name() const { return my_name; } std::string nameX() const { return my_name Hello; } void setName(const std::string name) { my_name name; } private: int my_age; std::string my_name; };所以这个析构函数在对象生命周期结束时会打印[Destructor] age......2.对象C 中的对象是类的实例化实体通过类定义的数据类型包含成员变量属性和成员函数方法。2.1对象定义在栈区栈区Stack由编译器自动分配和释放存储局部变量、函数参数等。栈区内存分配速度快但空间有限。当函数调用结束时栈帧自动释放。特点内存分配连续效率高。空间有限通常几MB超出会导致栈溢出Stack Overflow。生命周期由作用域控制自动管理。#include App.h #include iostream class MyClass { public: // Default constructor MyClass(): MyClass(0, default){ std::cout [Constructor] age0, namedefault std::endl; } // Parameter constructor MyClass(int age, const std::string name): my_age(age), my_name(name){ std::cout [Constructor] age age , name name std::endl; } // Copy Constructor (Deep Copy) - copy data from another object // Copies member variables to create independent objects MyClass(const MyClass other) : my_age(other.my_age), my_name(other.my_name) { std::cout [CopyConstructor] Copied age my_age , name my_name std::endl; } // Move Constructor (Move Semantics) - steal resources from another object // Transfer data ownership instead of copying MyClass(MyClass other) noexcept : my_age(other.my_age), my_name(std::move(other.my_name)) { std::cout [MoveConstructor] Moved age my_age , name my_name std::endl; other.my_age 0; other.my_name ; // Clear source object } // Destructor - called when object is destroyed ~MyClass() { std::cout [Destructor] age my_age , name my_name std::endl; } int age() const { return my_age; } const std::string name() const { return my_name; } std::string nameX() const { return my_name Hello; } void setName(const std::string name) { my_name name; } private: int my_age; std::string my_name; }; int main() { //1.定义在栈区超出作用域自动销毁生命周期结束自动释放内存 std::cout 1. Stack Object Lifetime std::endl; { std::cout Entering scope std::endl; MyClass obj1(40, Stack); std::cout Exiting scope, obj1 auto destroyed: std::endl; } // - Scope ends, auto call destructor std::cout \n Program End std::endl; return 0; }这里在{}栈区里创建了一个对象终端打印 1. Stack Object Lifetime Entering scope [Constructor] age40, nameStack Exiting scope, obj1 auto destroyed: [Destructor] age40, nameStack[Constructor]是我们之前的初始化列表中的函数在这里初始化时被调用[Destructor]是我们之前的析构函数在对象生命周期结束后被调用q(≧▽≦q)2.2对象定义在堆区堆区由程序员手动分配和释放或通过智能指针管理空间较大但分配速度较慢。需使用new/delete或malloc/free操作。就是纯手动申请的(╹ڡ╹ )特点空间灵活仅受系统内存限制。分配和释放需手动管理易出现内存泄漏或野指针。生命周期由程序员控制。...... //2.定义在堆区需要手动释放内存生命周期结束手动释放内存 std::cout \n 2. Heap Object Lifetime std::endl; { std::cout Using new to create object std::endl; MyClass* obj2 new MyClass(45, Heap); std::cout Manual delete to destroy: std::endl; delete obj2; // - Manual call destructor } ......日志打印 2. Heap Object Lifetime Using new to create object [Constructor] age45, nameHeap Manual delete to destroy: [Destructor] age45, nameHeap把delete注释掉后日志 2. Heap Object Lifetime Using new to create object [Constructor] age45, nameHeap Manual delete to destroy:可以看到析构函数没有被调用也就意味着堆区的该成员没有被释放这样就会造成内存泄漏X﹏X2.3对象的使用对象的使用比较简单这里不过多赘述简单说一下\(︶*\))在创建对象时的不同导致使用对象的不同方法//3.如何访问成员 std::cout \n 3. Class Members std::endl; { MyClass obj3(50, MemberAccess); std::cout Accessing members: age obj3.age() , name obj3.name() std::endl; std::unique_ptr MyClass obj4 std::make_uniqueMyClass(55, UniquePtr); std::cout Accessing members via unique_ptr: age obj4-age() , name obj4-name() std::endl; } // - Scope ends, unique_ptr auto call delete to release memory2.4匿名对象匿名对象Anonymous Object是指未被命名的临时对象通常直接在表达式中创建并使用生命周期仅限于当前语句。匿名对象常用于简化代码或作为函数参数传递。也就是创建后就被销毁生命周期最短(∪.∪ )...zzz//4.匿名对象创建后立即销毁生命周期最短 std::cout \n 4. Anonymous Object std::endl; { // Anonymous object: MyClass(25, Anonymous) has no variable name // Created immediately destroyed, shortest lifetime MyClass(25, Anonymous); // - Create now, destroy now std::cout Anonymous object destroyed\n std::endl; } 4. Anonymous Object [Constructor] age25, nameAnonymous [Destructor] age25, nameAnonymous Anonymous object destroyed可以看出创建完就嗝屁了~2.5深拷贝、浅拷贝在C中深拷贝Deep Copy和浅拷贝Shallow Copy是对象复制的两种方式主要区别在于对指针或动态分配内存的处理。浅拷贝仅复制对象的成员变量的值包括指针的地址指向同一块内存。原对象和拷贝对象的指针成员指向同一资源可能导致重复释放或内存泄漏。深拷贝不仅复制成员变量的值还为指针成员分配新的内存空间并复制其指向的内容。原对象和拷贝对象的指针成员指向独立的内存区域。所以一般使用是使用深拷贝。//• 深拷贝复制数据值 → 两个对象独立 //• 浅拷贝只复制指针 → 两个对象共享同一块内存 ❌(危险) std::cout 5. Deep Copy Demo std::endl; { MyClass obj1(30, Original); MyClass obj2 obj1; // Copy constructor performs deep copy std::cout obj1: obj1.age() , obj1.name() std::endl; std::cout obj2: obj2.age() , obj2.name() std::endl; std::cout obj1 address: obj1 , obj2 address: obj2 std::endl; std::cout \n--- Modifying obj1s name --- std::endl; obj1.setName(Modified); std::cout After obj1.setName(Modified): std::endl; std::cout obj1: obj1.age() , obj1.name() std::endl; std::cout obj2: obj2.age() , obj2.name() std::endl; std::cout \nKey point: obj2s name remains Original (deep copy guarantees independent strings) std::endl; std::cout If this were shallow copy, obj2 would also show Modified\n std::endl; } 5. Deep Copy Demo [Constructor] age30, nameOriginal [CopyConstructor] Copied age30, nameOriginal obj1: 30, Original obj2: 30, Original obj1 address: 000000C8A58FF448, obj2 address: 000000C8A58FF498 --- Modifying obj1s name --- After obj1.setName(Modified): obj1: 30, Modified obj2: 30, Original指针成员指向的地址独立深拷贝修改obj1的nameobj2不会被修改。2.5对象移动移动语义是C11引入的重要特性旨在通过转移资源所有权而非复制数据来提升性能。其核心思想是将资源如堆内存从临时对象右值高效转移到目标对象避免不必要的深拷贝。//移动语义资源转移 → 源对象变为空 std::cout 6. Move Semantics Demo std::endl; { MyClass obj3(35, Source); std::cout \n--- Executing Move Constructor --- std::endl; MyClass obj4 std::move(obj3); // - Call move constructor, obj3 resources stolen std::cout After move obj3: obj3.age() , name obj3.name() (cleared) std::endl; std::cout After move obj4: obj4.age() , obj4.name() std::endl; std::cout Key point: Move semantics steals source object resources, source becomes empty\n std::endl; } 6. Move Semantics Demo [Constructor] age35, nameSource --- Executing Move Constructor --- [MoveConstructor] Moved age35, nameSource After move obj3: 0, name (cleared) After move obj4: 35, Source Key point: Move semantics steals source object resources, source becomes empty [Destructor] age35, nameSource [Destructor] age0, name Program End 可以看到使用move后obj3已经被销毁了不过没有调用析构函数o((ω ))o3.多态多态Polymorphism是面向对象编程的三大特性之一封装、继承、多态它允许不同类的对象对同一消息做出不同的响应。简单来说就是“一个接口多种实现”。不是我就问了一下多态是啥这agent直接给我写完了Σ(っ °Д °;)っ3.1 多态的基本概念在C中多态主要通过虚函数virtual function实现。当基类指针或引用指向派生类对象时通过虚函数可以调用到派生类中重写的函数而不是基类的版本。静态多态编译时多态通过函数重载和运算符重载实现在编译时确定调用哪个函数。动态多态运行时多态通过虚函数和继承实现在运行时根据对象的实际类型确定调用哪个函数。3.2 虚函数与纯虚函数虚函数使用virtual关键字声明允许在派生类中被重写。纯虚函数在声明时加上 0表示该函数没有实现包含纯虚函数的类称为抽象类不能实例化。#include iostream // 基类 class Animal { public: // 虚函数 virtual void makeSound() { std::cout Animal makes a sound std::endl; } // 纯虚函数 - 抽象类 virtual void move() 0; virtual ~Animal() {} // 虚析构函数 }; // 派生类 Dog class Dog : public Animal { public: void makeSound() override { std::cout Dog barks: Woof! Woof! std::endl; } void move() override { std::cout Dog runs on four legs std::endl; } }; // 派生类 Cat class Cat : public Animal { public: void makeSound() override { std::cout Cat meows: Meow~ std::endl; } void move() override { std::cout Cat walks gracefully std::endl; } }; int main() { // 基类指针指向派生类对象 Animal* animal1 new Dog(); Animal* animal2 new Cat(); // 多态调用 animal1-makeSound(); // 输出: Dog barks: Woof! Woof! animal1-move(); // 输出: Dog runs on four legs animal2-makeSound(); // 输出: Cat meows: Meow~ animal2-move(); // 输出: Cat walks gracefully delete animal1; delete animal2; return 0; }3.3 多态的优势代码复用通过基类接口统一处理不同派生类对象扩展性好新增派生类不需要修改现有代码接口统一提供一致的编程接口运行时灵活性程序运行时可以动态决定调用哪个函数3.4 QT中的多态应用在QT框架中多态被广泛应用。比如事件处理机制#include iostream #include vector #include thread #include chrono #include random class Event { public: enum Type { MouseEvent, KeyEvent, TouchEvent, Max_Type }; Event(Type type) : type(type) {} Type getType() const { return type; } private: Type type{}; }; class Object { friend class Application; public: void setName(const std::string name) { this-name name; } const std::string getName() const { return name; } protected: // 虚函数 - 事件处理 virtual bool event(Event* ev) { return false; } private: std::string name; }; class Widget : public Object { public: protected: // 重写基类的虚函数 bool event(Event* ev) override { switch (ev-getType()) { case Event::MouseEvent: std::cout Widget received MouseEvent std::endl; break; case Event::KeyEvent: std::cout Widget received KeyEvent std::endl; break; case Event::TouchEvent: std::cout Widget received TouchEvent std::endl; break; default: std::cout Widget received unknown event std::endl; break; } return true; } }; class Button : public Widget { protected: bool event(Event* ev) override { if (ev-getType() Event::MouseEvent) { std::cout Button clicked! Handling MouseEvent specially. std::endl; return true; } // 其他事件交给父类处理 return Widget::event(ev); } }; class Application : public Object { public: Application(int argc, char* argv[]) { std::cout Application created with argc arguments. std::endl; } int exec() { std::default_random_engine eng(std::random_device{}()); for (int i 0; i 10; i) { // 生成随机事件 Event ev(Event::Type(eng() % Event::Max_Type)); // 多态调用通过基类指针调用不同派生类的event函数 for (auto obj : objects) { obj-event(ev); } std::this_thread::sleep_for(std::chrono::milliseconds(300)); } return 0; } void addObject(std::shared_ptrObject obj) { objects.push_back(obj); } private: std::vectorstd::shared_ptrObject objects; }; int main(int argc, char* argv[]) { Application app(argc, argv); // 添加不同类型的对象 app.addObject(std::make_sharedWidget()); app.addObject(std::make_sharedButton()); app.addObject(std::make_sharedWidget()); app.addObject(std::make_sharedButton()); // 运行应用程序演示多态 return app.exec(); }3.5 运行结果示例Application created with 1 arguments. Widget received MouseEvent Button clicked! Handling MouseEvent specially. Widget received MouseEvent Button clicked! Handling MouseEvent specially. Widget received KeyEvent Button received KeyEvent Widget received KeyEvent Button received KeyEvent Widget received TouchEvent Button received TouchEvent Widget received TouchEvent Button received TouchEvent Widget received MouseEvent Button clicked! Handling MouseEvent specially. Widget received MouseEvent Button clicked! Handling MouseEvent specially.3.6 多态使用注意事项虚析构函数如果基类有虚函数应该将析构函数也声明为虚函数确保正确释放派生类资源override关键字C11引入明确表示重写基类虚函数增加代码可读性和安全性final关键字C11引入表示该函数不能在派生类中被进一步重写抽象类包含纯虚函数的类不能实例化只能作为接口使用多态是C面向对象编程的核心特性在QT框架中被大量使用。理解多态有助于编写更灵活、可扩展的代码特别是在GUI编程和事件处理中尤为重要。结语所以要学习QT不需要特别会C但需要了解一些在QT中常用的C基本知识✪ ω ✪PS终于写完了最近在用AI写一个QT项目目前AI的UI设计和整体架构做得都挺不错的不过具体实现一些复杂的功能表现还不是很好有没有大佬教一下UwU
返回列表