自学c++面向对象知识点2

学习目标:

自学c++面向对象知识点。


第五章 数据的共享与保护

1.静态数据成员
在C++中,静态数据成员是属于类而不是类的实例的成员。静态数据成员在所有类的实例之间共享,而不是每个实例都有自己的拷贝。它在类的整个生命周期内存在,而不是依赖于特定实例的创建和销毁。

静态数据成员的声明和定义方式与普通数据成员有些不同,它需要使用 static 关键字来标识。通常,**静态数据成员的初始化和声明是在类的定义文件中进行的,而在类的实现文件中进行定义。**以下是一个简单的例子:

#include <iostream>

class MyClass {
public:
    // 普通成员函数
    void setNumber(int num) {
        number = num;
        staticNumber = num;  // 静态数据成员可以在普通成员函数中使用
    }

    // 静态成员函数
    static void printStaticNumber() {
        // 不能在静态成员函数中直接访问非静态数据成员
        // std::cout << "Number: " << number << std::endl;  // 错误

        // 可以直接访问静态数据成员
        std::cout << "Static Number: " << staticNumber << std::endl;
    }

private:
    int number;                   // 普通数据成员
    static int staticNumber;      // 静态数据成员
};

// 初始化静态数据成员
int MyClass::staticNumber = 0;

int main() {
    MyClass obj1, obj2;

    obj1.setNumber(42);
    obj2.setNumber(123);

    MyClass::printStaticNumber();  // 通过类名调用静态成员函数

    return 0;
}

在这个例子中,number 是普通的数据成员,而 staticNumber 是静态数据成员。注意如何初始化静态数据成员,它通常在类的实现文件中进行,而不是在类的声明文件中。

静态数据成员的特性:

  • 所有类的实例共享同一份静态数据成员。
  • 静态数据成员可以通过类名直接访问,而不需要创建类的实例。
  • 静态数据成员的生命周期与程序的生命周期相同,它们在程序启动时被初始化,在程序结束时被销毁。
  • 静态数据成员是私有类型也可以直接初始化。

2.静态函数成员
(1)不依赖于类的实例,因此可以直接通过类名调用
(2)不能直接访问类的非静态数据成员和非静态成员函数,必须通过对象名。静态成员函数只能直接访问类的静态数据成员和静态成员函数。
(3)静态成员函数不能使用 this 指针,因为它没有特定的对象实例。

3.友元函数
在C++中,友元函数(Friend Functions)是与类相关联的非成员函数,它被允许访问类的私有成员。友元函数通常用于提供某些与类关联的操作,但这些操作不是类的成员函数。

友元函数的声明和定义通常在类的声明中,但它不是类的成员函数。它可以访问类的私有成员,就像它是类的朋友一样。

以下是一个简单的例子:

#include <iostream>

class MyClass {
private:
    int privateData;

public:
    MyClass(int data) : privateData(data) {}

    // 友元函数的声明
    friend void friendFunction(const MyClass& obj);
};

// 友元函数的定义
void friendFunction(const MyClass& obj) {
    std::cout << "Friend Function: Accessing private data - " << obj.privateData << std::endl;
}

int main() {
    MyClass myObject(42);

    // 调用友元函数
    friendFunction(myObject);

    return 0;
}

在这个例子中,friendFunction 是一个友元函数,它可以访问 MyClass 类的私有成员 privateData,即使它不是 MyClass 的成员函数。

友元函数的特性:

  • 友元函数声明通常在类的声明中,但它的定义在类的外部。
  • 友元函数可以访问类的所有成员,包括私有成员。
  • 友元函数不是类的成员函数,但它可以被类友好地访问。
  • 友元函数可以是另外一个类的成员函数,使用时要通过相应的类或对象名访问。

需要注意的是,友元函数的使用应该谨慎,因为它打破了封装的原则,增加了类的耦合性。友元函数应该在确实需要对类的私有成员进行访问时使用,而不是滥用。

4.友元类
在C++中,友元类(Friend Class)是一种机制,允许一个类将另一个类声明为其友元,从而使得友元类能够访问声明它为友元的类的私有成员。这可以用于提供更灵活的访问权限,但同样需要慎重使用,以避免破坏封装性。

以下是一个简单的例子:

#include <iostream>

// 前向声明
class FriendClass;

class MyClass {
private:
    int privateData;

public:
    MyClass(int data) : privateData(data) {}

    // 友元类的声明
    friend class FriendClass;
};

// 友元类的定义
class FriendClass {
public:
    // 友元类的成员函数可以访问 MyClass 的私有成员
    void accessPrivateData(const MyClass& obj) {
        std::cout << "FriendClass accessing private data: " << obj.privateData << std::endl;
    }
};

int main() {
    MyClass myObject(42);

    FriendClass friendObject;
    friendObject.accessPrivateData(myObject);

    return 0;
}

在这个例子中,FriendClass 被声明为 MyClass 的友元类。因此,FriendClass 的成员函数 accessPrivateData 可以访问 MyClass 的私有成员 privateData

友元类的特性:

  • 友元类的声明通常出现在类的声明中,而友元类的定义出现在类的外部。
  • 友元类的所有成员函数都可以访问声明它为友元的类的私有成员。
  • 友元关系是单向的,如果类 A 声明类 B 为友元,不一定意味着类 B 也声明了类 A 为友元。
  • 友元关系也是不可以传递的。

需要注意的是,过度使用友元类可能导致类之间的耦合性增加,降低代码的封装性。友元关系应该在确实需要对私有成员进行访问的情况下使用,并谨慎选择何时使用友元关系。

5.const:常对象必须进行初始化,且不可以被更新。
(1)如果将一个对象声明为常对象,则只能调用常成员函数(这也是常对象唯一的对外接口方式),而不能调用其他成员函数。
(2)常成员函数不能更新目的对象的数据成员,也不能调用没有const修饰的成员函数。
(3)const关键字可以用于重载。

void print();
void print() const;

(4)常数据成员:通过构造函数进行初始化,只能通过初始化列表。

//类中定义
const int a;
//类外初始化列表
A::A(int i):a(i){};

(5)常引用:其所引用的对象不能被更新。非const的引用只能绑定到普通的对象,不能绑定到常对象。而常引用可以绑定到普通的对象,也可以绑定到常对象。

6.外部变量与外部函数
在编程中,"外部变量"和"外部函数"通常指的是在一个文件中定义的变量和函数,然后在其他文件中通过声明来引用它们。

外部变量

外部变量是在一个源文件中定义的变量,然后通过 extern 关键字在其他源文件中进行声明以便引用。外部变量的生命周期跨越整个程序,可以在多个文件之间共享。

在一个文件中定义外部变量:

// file1.c
int globalVariable = 42;

在另一个文件中通过 extern 关键字声明并引用外部变量:

// file2.c
extern int globalVariable;

外部函数

外部函数是在一个源文件中定义的函数,然后通过函数原型(函数声明)在其他文件中进行声明以便引用。外部函数允许在一个文件中定义函数的实现,而在其他文件中调用该函数。

在一个文件中定义外部函数:

// file1.c
#include <stdio.h>

void externalFunction() {
    printf("This is an external function.\n");
}

在另一个文件中通过函数原型声明并引用外部函数:

// file2.c
extern void externalFunction();

int main() {
    externalFunction(); // 调用外部函数
    return 0;
}

第六章 数组与指针

1.数组作为参数时,传递的是地址;若在被调函数中对形参数组元素值进行改变,主调函数中实参数组的相应元素值也会改变。

2.this指针
在C++中,this 指针是一个特殊的指针,指向当前对象的地址。它是一个隐含的参数(隐含于每一个类的非静态成员函数中的特殊指针,包括构造函数和析构函数),用于指向被调用的成员函数所操作的对象。当成员函数被调用时,this 指针被隐式传递给成员函数,指向调用该函数的对象。

以下是关于 this 指针的一些重要概念:

成员函数内部使用:
在成员函数内部,可以通过 this 指针来访问对象的成员变量和成员函数。这对于区分成员变量和函数参数同名的情况很有帮助。

class MyClass {
public:
    void printAddress() {
        std::cout << "Address of the object: " << this << std::endl;
    }
};

隐式传递:
当对象调用其成员函数时,this 指针被隐式传递给成员函数。例如:

MyClass obj1, obj2;
obj1.printAddress();  // this 指向 obj1
obj2.printAddress();  // this 指向 obj2

用于返回当前对象:
在成员函数中,this 指针可以用于返回当前对象的引用,从而支持链式调用。

class MyClass {
public:
    MyClass& setX(int x) {
        this->x = x;
        return *this;
    }

private:
    int x;
};

MyClass obj;
obj.setX(42);  // 链式调用

在静态成员函数中不可用:
静态成员函数是与类关联而不与对象关联的函数,因此在静态成员函数中无法使用 this 指针。

class MyClass {
public:
    static void staticFunction() {
        // 无法使用 this 指针
    }
};

this 指针的使用使得在成员函数中能够访问对象的成员变量和成员函数,同时也帮助解决了成员变量和函数参数同名的问题。在大多数情况下,程序员无需显式地使用 this 指针,因为它在成员函数内部自动被引入。

3.指向类的非静态成员的指针
指向类的非静态成员的指针是指针,它可以用来指向类的非静态成员变量或非静态成员函数。这种指针通常使用类类型和成员类型进行声明。以下是一些例子:

(1) 指向非静态成员变量的指针
格式:类型说明符 类名::*指针名;

#include <iostream>

class MyClass {
public:
    int myVariable;
};

int main() {
    MyClass obj;
    obj.myVariable = 42;

    // 声明指向非静态成员变量的指针
    int MyClass::*ptr = &MyClass::myVariable;

    // 使用指针访问成员变量
    std::cout << "Value of myVariable: " << obj.*ptr << std::endl;

    return 0;
}

在这个例子中,int MyClass::*ptr 是一个指向 MyClass 类的非静态成员变量 myVariable 的指针。通过 obj.*ptr,我们可以访问 obj 对象的 myVariable 成员变量。

(2)指向非静态成员函数的指针:
格式:类型说明符 (类名::*指针名)(参数表)

#include <iostream>

class MyClass {
public:
    void printMessage() {
        std::cout << "Hello, World!" << std::endl;
    }
};

int main() {
    MyClass obj;

    // 声明指向非静态成员函数的指针
    void (MyClass::*funcPtr)() = &MyClass::printMessage;

    // 使用指针调用成员函数
    (obj.*funcPtr)();

    return 0;
}

在这个例子中,void (MyClass::*funcPtr)() 是一个指向 MyClass 类的非静态成员函数 printMessage 的指针。通过 (obj.*funcPtr)(),我们可以调用 obj 对象的 printMessage 成员函数。

注:声明了指针后,还需赋值。赋值的格式为:指针名 = &类名::数据/函数成员名
调用的格式:对象名.*指针名 或 对象指针名->类成员指针名;函数的话,在后面加一个(参数表)即可。

4.指向类的静态成员的指针
对于类的静态成员,不需要创建类的实例就可以访问它们。因此,指向类的静态成员的指针与指向非静态成员的指针有所不同。以下是指向类的静态成员的指针的例子:

#include <iostream>

class MyClass {
public:
    static int staticVariable;

    static void staticFunction() {
        std::cout << "Static function called." << std::endl;
    }
};

// 初始化静态成员变量
int MyClass::staticVariable = 42;

int main() {
    // 声明指向静态成员变量的指针
    int* ptrStaticVariable = &MyClass::staticVariable;

    // 使用指针访问静态成员变量
    std::cout << "Value of staticVariable: " << *ptrStaticVariable << std::endl;

    // 声明指向静态成员函数的指针
    void (*ptrStaticFunction)() = &MyClass::staticFunction;

    // 使用指针调用静态成员函数
    ptrStaticFunction();

    return 0;
}

在这个例子中,int* ptrStaticVariable = &MyClass::staticVariable; 是一个指向 MyClass 类的静态成员变量 staticVariable 的指针。通过 *ptrStaticVariable 我们可以访问 staticVariable 的值。

void (*ptrStaticFunction)() = &MyClass::staticFunction; 是一个指向 MyClass 类的静态成员函数 staticFunction 的指针。通过 ptrStaticFunction() 我们可以调用 staticFunction

需要注意的是,静态成员函数和静态成员变量是与类关联而不与对象关联的,因此在声明和使用指针时不需要实例化对象。

5.深层复制与浅层复制
深层复制(Deep Copy)和浅层复制(Shallow Copy)是在编程中常用于描述复制对象或数据结构时的两种不同方式。

深层复制(Deep Copy):

深层复制是指在复制对象时,会复制对象所包含的所有数据,包括对象内部的所有子对象。这意味着新创建的对象与原始对象是完全独立的,对其中一个对象的修改不会影响到另一个对象。

在深层复制中,每个对象及其所有相关的对象都会被递归地复制。这通常涉及到对象的拷贝构造函数或者自定义的复制逻辑。深层复制通常用于确保对象之间的独立性,防止共享相同的内存引用。

#include <iostream>
#include <string>

class DeepCopyExample {
public:
    DeepCopyExample(const std::string& str) : data(new std::string(str)) {}

    // 深层复制的拷贝构造函数
    DeepCopyExample(const DeepCopyExample& other) : data(new std::string(*(other.data))) {}

    void printData() const {
        std::cout << *data << std::endl;
    }

private:
    std::string* data;
};

int main() {
    DeepCopyExample obj1("Hello");
    DeepCopyExample obj2 = obj1;  // 深层复制
    obj2.printData();  // 输出 "Hello"

    // 修改 obj1 中的数据不会影响 obj2
    obj1.printData();  // 输出 "Hello"
    obj1.printData();  // 输出 "Hello"

    return 0;
}

浅层复制(Shallow Copy):

浅层复制是指在复制对象时,只复制对象本身,而不复制对象内部包含的引用或指针。这意味着新创建的对象与原始对象共享相同的子对象,对其中一个对象的修改会影响到另一个对象。

在浅层复制中,只有对象本身被复制,而对象内部的指针或引用则被直接复制,两个对象共享同一块内存。因此该空间被两次释放,于是导致运行错误。

#include <iostream>
#include <string>

class ShallowCopyExample {
public:
    ShallowCopyExample(const std::string& str) : data(new std::string(str)) {}

    // 浅层复制的拷贝构造函数
    ShallowCopyExample(const ShallowCopyExample& other) : data(other.data) {}

    void printData() const {
        std::cout << *data << std::endl;
    }

private:
    std::string* data;
};

int main() {
    ShallowCopyExample obj1("Hello");
    ShallowCopyExample obj2 = obj1;  // 浅层复制
    obj2.printData();  // 输出 "Hello"

    // 修改 obj1 中的数据会影响 obj2
    obj1.printData();  // 输出 "Hello"
    obj1.printData();  // 输出 "Hello"

    return 0;
}

在选择深层复制或浅层复制时,需要根据程序的需求和数据结构的特性来决定。深层复制通常比较安全,但可能会导致额外的内存开销。浅层复制可能更高效,但需要注意共享的对象可能会导致不希望的副作用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值