c++ 学习

Qt入门之基础篇 ( 二 ) :Qt项目建立、编译、运行和发布过程解析 - 谢维开 - 博客园

c++ for遍历向量举例 ,详细说明

当然,下面是一个使用C++中的for循环来遍历std::vector(向量)的详细例子和说明。

例子:遍历并打印向量中的元素

cppCopy Code

#include <iostream>

#include <vector>

int main() {

    // 创建一个整数向量并初始化

    std::vector<int> numbers = {1, 2, 3, 4, 5};

    // 使用索引for循环遍历向量

    std::cout << "Using index-based for loop:" << std::endl;

    for (size_t i = 0; i < numbers.size(); ++i) {

        std::cout << numbers[i] << " ";

    }

    std::cout << std::endl;

    // 使用范围for循环遍历向量

    std::cout << "Using range-based for loop:" << std::endl;

    for (int number : numbers) {

        std::cout << number << " ";

    }

    std::cout << std::endl;

    return 0;

}

详细说明

  1. 创建向量‌:

cppCopy Code

std::vector<int> numbers = {1, 2, 3, 4, 5};

    • 这行代码创建了一个std::vector<int>类型的向量,名为numbers,并用列表初始化语法给它赋了初值 {1, 2, 3, 4, 5}。
  1. 索引for循环‌:

cppCopy Code

for (size_t i = 0; i < numbers.size(); ++i) {

    std::cout << numbers[i] << " ";

}

    • 这个循环使用索引来遍历向量。size_t是一种无符号整数类型,通常用于表示大小和计数。
    • numbers.size()返回向量的元素数量。
    • 在每次迭代中,i从0开始递增,直到达到向量的末尾。
    • numbers[i]访问向量中索引为i的元素,并将其打印出来。
  1. 范围for循环‌(C++11及更高版本):

cppCopy Code

for (int number : numbers) {

    std::cout << number << " ";

}

    • 这个循环使用范围for语法来遍历向量。它对向量中的每个元素执行一次循环体,并将元素的值赋给number变量。
    • 这种方法更简洁,不需要显式地处理索引。

输出

运行上述代码将产生以下输出:

textCopy Code

Using index-based for loop:

1 2 3 4 5

Using range-based for loop:

1 2 3 4 5

总结

  • 使用索引for循环时,你需要手动处理索引和边界条件。
  • 使用范围for循环时,循环会自动处理这些细节,使代码更简洁、更易读。
  • 在选择使用哪种循环时,要考虑你的具体需求,比如是否需要访问元素的索引或者是否需要修改向量中的元素。如果只需要访问元素的值,范围for循环通常是更好的选择。

C++ 多态函数举例,详细说明



在C++中,多态性(Polymorphism)是面向对象编程中的一个重要特性,它允许你使用统一的接口处理不同类型的对象。多态性通常通过继承和虚函数来实现。下面是一个详细的例子来说明C++中的多态函数。

例子:动物王国的多态性

假设我们有一个基类 Animal,以及两个派生类 Dog 和 Cat。我们希望针对不同类型的动物对象,通过同一个接口来调用它们特有的行为,比如 makeSound()。

1. 定义基类 Animal

cppCopy Code

#include <iostream>

#include <vector>

// 基类

class Animal {

public:

    // 虚析构函数,确保派生类的析构函数也能被正确调用

    virtual ~Animal() {}

    // 纯虚函数,让 Animal 成为抽象类

    virtual void makeSound() const = 0;

};

2. 定义派生类 Dog 和 Cat

cppCopy Code

// 派生类 Dog

class Dog : public Animal {

public:

    void makeSound() const override {

        std::cout << "Woof! Woof!" << std::endl;

    }

};

// 派生类 Cat

class Cat : public Animal {

public:

    void makeSound() const override {

        std::cout << "Meow! Meow!" << std::endl;

    }

};

3. 使用多态性

cppCopy Code

int main() {

    // 创建一个 Animal 指针的向量,用于存储不同类型的动物对象

    std::vector<Animal*> animals;

    // 向向量中添加 Dog 和 Cat 对象

    animals.push_back(new Dog());

    animals.push_back(new Cat());

    // 通过遍历向量并调用虚函数 makeSound(),展示多态性

    for (const auto& animal : animals) {

        animal->makeSound();

    }

    // 释放动态分配的内存

    for (auto& animal : animals) {

        delete animal;

    }

    animals.clear();

    return 0;

}

说明

  1. 基类 Animal‌:
    • 定义了一个纯虚函数 makeSound(),这使得 Animal 成为一个抽象类,不能直接实例化。
    • 定义了虚析构函数,确保派生类对象被正确销毁。
  2. 派生类 Dog 和 Cat‌:
    • 这两个类都继承了 Animal,并实现了各自的 makeSound() 函数。
  3. 多态性的使用‌:
    • 创建一个 Animal 指针的向量 animals。
    • 向向量中添加 Dog 和 Cat 对象。注意这里是通过基类指针来存储派生类对象,这是实现多态性的关键。
    • 遍历向量并调用 makeSound() 函数。由于 makeSound() 是虚函数,调用时会根据实际对象的类型调用对应的函数,这就是多态性的体现。
    • 最后,释放动态分配的内存,防止内存泄漏。

通过这种方式,你可以使用基类指针或引用来操作不同类型的派生类对象,并通过虚函数来实现不同的行为。这就是C++中多态性的强大之处。

初始化用法  :value(0)

在C++中,当使用new运算符创建对象时,你可以在创建对象时初始化其成员变量。这可以通过在new表达式后面使用括号提供构造函数的参数来实现。如果类有默认构造函数且你没有提供任何参数,那么括号可以省略。

以下是一些示例,展示了如何使用new运算符来创建对象并初始化成员变量:

示例 1:使用默认构造函数

假设有一个简单的类MyClass:

cppCopy Code

class MyClass {

public:

    int value;

    MyClass() : value(0) {

        // 默认构造函数,将value初始化为0

    }

};

int main() {

    // 使用new运算符创建对象,调用默认构造函数

    MyClass* obj = new MyClass;

    // 访问成员变量

    std::cout << "Value: " << obj->value << std::endl;

    // 释放内存

    delete obj;

    return 0;

}

示例 2:使用带参数的构造函数

假设MyClass有一个带参数的构造函数:

cppCopy Code

class MyClass {

public:

    int value;

    MyClass(int val) : value(val) {

        // 带参数的构造函数,将value初始化为传入的参数

    }

};

int main() {

    // 使用new运算符创建对象,并传递参数给构造函数

    MyClass* obj = new MyClass(42);

    // 访问成员变量

    std::cout << "Value: " << obj->value << std::endl;

    // 释放内存

    delete obj;

    return 0;

}

示例 3:初始化多个成员变量

假设MyClass有多个成员变量,并且你希望初始化它们:

cppCopy Code

class MyClass {

public:

    int value1;

    double value2;

    MyClass(int val1, double val2) : value1(val1), value2(val2) {

        // 带参数的构造函数,初始化多个成员变量

    }

};

int main() {

    // 使用new运算符创建对象,并传递多个参数给构造函数

    MyClass* obj = new MyClass(42, 3.14);

    // 访问成员变量

    std::cout << "Value1: " << obj->value1 << ", Value2: " << obj->value2 << std::endl;

    // 释放内存

    delete obj;

    return 0;

}

示例 4:初始化列表(C++11及以上)

在C++11及更高版本中,你还可以使用初始化列表来初始化对象:

cppCopy Code

class MyClass {

public:

    int value1;

    double value2;

    // 可以省略构造函数,使用默认的初始化列表

};

int main() {

    // 使用初始化列表创建对象

    MyClass* obj = new MyClass{42, 3.14};

    // 访问成员变量

    std::cout << "Value1: " << obj->value1 << ", Value2: " << obj->value2 << std::endl;

    // 释放内存

    delete obj;

    return 0;

}

总结

  • 使用new运算符创建对象时,可以通过构造函数初始化成员变量。
  • 如果类有默认构造函数,可以省略参数括号。
  • 可以使用带参数的构造函数和初始化列表来初始化成员变量。
  • 不要忘记在main函数结束前使用delete运算符释放动态分配的内存,以防止内存泄漏。


 

c++ dynamic_cast 用法



dynamic_cast 是 C++ 中用于在运行时安全地进行多态类型转换的操作符。它特别有用在涉及继承层次结构的指针或引用之间转换的场景。dynamic_cast 可以保证如果转换是非法的,它将返回 nullptr(对于指针)或抛出 std::bad_cast 异常(对于引用)。

以下是 dynamic_cast 的基本用法:

1. 用于指针

当你试图将一个基类指针转换为派生类指针时,可以使用 dynamic_cast。如果转换是有效的,dynamic_cast 返回指向派生类的指针;如果转换无效,它返回 nullptr。

cppCopy Code

#include <iostream>

#include <typeinfo>

class Base {

public:

    virtual ~Base() {}

};

class Derived : public Base {

    void derivedMethod() {}

};

int main() {

    Base* basePtr = new Derived();

   

    Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);

    if (derivedPtr) {

        std::cout << "Conversion successful." << std::endl;

        // 可以安全地调用 Derived 类的方法

        derivedPtr->derivedMethod();

    } else {

        std::cout << "Conversion failed." << std::endl;

    }

   

    delete basePtr;

    return 0;

}

2. 用于引用

dynamic_cast 也可以用于引用,但是如果转换失败,它会抛出 std::bad_cast 异常,因此需要使用 try-catch 块来捕获异常。

cppCopy Code

#include <iostream>

#include <typeinfo>

#include <stdexcept>

class Base {

public:

    virtual ~Base() {}

};

class Derived : public Base {

public:

    void derivedMethod() {}

};

int main() {

    Base& baseRef = *new Derived();

   

    try {

        Derived& derivedRef = dynamic_cast<Derived&>(baseRef);

        std::cout << "Conversion successful." << std::endl;

        // 可以安全地调用 Derived 类的方法

        derivedRef.derivedMethod();

    } catch (const std::bad_cast& e) {

        std::cout << "Conversion failed: " << e.what() << std::endl;

    }

   

    delete &baseRef;

    return 0;

}

注意事项

  • dynamic_cast 仅适用于含有虚函数的类(即多态类型)。如果类中不包含虚函数,使用 dynamic_cast 将导致编译错误。
  • 相对于静态类型转换(如 static_cast),dynamic_cast 的开销更高,因为它在运行时检查类型的安全性。
  • 当对指针进行 dynamic_cast 时,如果转换失败返回 nullptr,这要求程序员检查转换后的指针是否为空。
  • 当对引用进行 dynamic_cast 时,如果转换失败则抛出异常,这要求使用异常处理机制来捕获并处理失败。

dynamic_cast 提供了在运行时安全地进行类型转换的方法,是处理 C++ 中复杂继承关系时的重要工具。

给我一个使用dynamic_cast的完整例子

当然,下面是一个使用 dynamic_cast 的完整例子,它展示了如何在 C++ 中利用 dynamic_cast 来实现运行时类型识别和安全的多态类型转换。

cppCopy Code

#include <iostream>

#include <typeinfo>

#include <exception>

// 基类

class Animal {

public:

    virtual ~Animal() {} // 虚析构函数,确保派生类的析构函数能够被调用

    virtual void speak() const = 0; // 纯虚函数,让Animal成为抽象类

};

// 派生类 Dog

class Dog : public Animal {

public:

    void speak() const override {

        std::cout << "Woof! Woof!" << std::endl;

    }

    void wagTail() const {

        std::cout << "Wagging tail..." << std::endl;

    }

};

// 派生类 Cat

class Cat : public Animal {

public:

    void speak() const override {

        std::cout << "Meow! Meow!" << std::endl;

    }

    void purr() const {

        std::cout << "Purring..." << std::endl;

    }

};

int main() {

    // 创建一个指向Animal的指针,实际指向Dog对象

    Animal* animalPtr = new Dog();

    // 尝试将Animal指针转换为Dog指针

    Dog* dogPtr = dynamic_cast<Dog*>(animalPtr);

    if (dogPtr) {

        std::cout << "Successfully cast to Dog." << std::endl;

        dogPtr->speak();      // 输出:Woof! Woof!

        dogPtr->wagTail();    // 输出:Wagging tail...

    } else {

        std::cout << "Failed to cast to Dog." << std::endl;

    }

    // 尝试将Animal指针转换为Cat指针

    Cat* catPtr = dynamic_cast<Cat*>(animalPtr);

    if (catPtr) {

        std::cout << "Successfully cast to Cat." << std::endl;

        catPtr->speak();

        catPtr->purr();

    } else {

        std::cout << "Failed to cast to Cat." << std::endl; // 预期输出

    }

    // 清理内存

    delete animalPtr;

    // 为了展示对引用的dynamic_cast,我们创建一个Animal的引用,实际引用Dog对象

    Animal& animalRef = *new Dog();

    try {

        // 尝试将Animal引用转换为Dog引用

        Dog& dogRef = dynamic_cast<Dog&>(animalRef);

        std::cout << "Successfully cast reference to Dog." << std::endl;

        dogRef.speak();      // 输出:Woof! Woof!

        dogRef.wagTail();    // 输出:Wagging tail...

    } catch (const std::bad_cast& e) {

        std::cout << "Failed to cast reference to Dog: " << e.what() << std::endl;

    }

    // 尝试将Animal引用转换为Cat引用(这将抛出异常)

    try {

        Cat& catRef = dynamic_cast<Cat&>(animalRef);

        std::cout << "Successfully cast reference to Cat." << std::endl;

        catRef.speak();

        catRef.purr();

    } catch (const std::bad_cast& e) {

        std::cout << "Failed to cast reference to Cat: " << e.what() << std::endl; // 预期输出

    }

    // 清理内存

    delete &animalRef;

    return 0;

}

在这个例子中,我们定义了一个基类 Animal 和两个派生类 Dog 和 Cat。Animal 类有一个纯虚函数 speak,这使得 Animal 成为一个抽象类。Dog 和 Cat 类分别重写了 speak 方法,并添加了各自的特有方法(wagTail 和 purr)。

在 main 函数中,我们首先创建了一个指向 Dog 对象的 Animal 指针,并使用 dynamic_cast 尝试将其转换为 Dog 指针和 Cat 指针。转换到 Dog 指针成功,而转换到 Cat 指针失败(返回 nullptr)。

接下来,我们创建了一个引用 Dog 对象的 Animal 引用,并使用 dynamic_cast 尝试将其转换为 Dog 引用和 Cat 引用。转换到 Dog 引用成功,而转换到 Cat 引用则抛出了 std::bad_cast 异常。

注意,在实际应用中,通常不建议使用裸指针和手动内存管理(如 new 和 delete)。更好的做法是使用智能指针(如 std::unique_ptr 或 std::shared_ptr)来自动管理内存。然而,为了简化示例,这里我们仍然使用了裸指针。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值