Basic Object-oriented Programming___CH_13

2023-10-27

13.1 — Welcome to object-oriented programming

Object-oriented programming (OOP) provides us with the ability to create objects that tie together both properties and behaviors into a self-contained, reusable package. This leads to code that looks more like this:

you.driveTo(work);

Note that the term “object” is overloaded a bit, and this causes some amount of confusion. In traditional programming, an object is a piece of memory to store values. And that’s it. In object-oriented programming, an “object” implies that it is both an object in the traditional programming sense, and that it combines both properties and behaviors. From this point forward, when we use the term “object”, we’ll be referring to “objects” in the object-oriented sense.

13.2 — Classes and class members

Here is an example of a struct used to hold a date:

struct DateStruct
{
    int year {};
    int month {};
    int day {};
};

Enumerated types and data-only structs (structs that only contain variables) represent the traditional non-object-oriented programming world, as they can only hold data. We can create and initialize this struct as follows:

DateStruct today { 2020, 10, 14 }; // use uniform initialization
Now, if we want to print the date to the screen (something we probably want to do a lot), it makes sense to write a function to do this. Here’s a full program:

#include <iostream>

struct DateStruct
{
    int year {};
    int month {};
    int day {};
};

void print(const DateStruct& date)
{
    std::cout << date.year << '/' << date.month << '/' << date.day;
}

int main()
{
    DateStruct today { 2020, 10, 14 }; // use uniform initialization

    today.day = 16; // use member selection operator to select a member of the struct
    print(today);

    return 0;
}

This program prints:

2020/10/16

Classes

In C++, classes and structs are essentially the same. In fact, the following struct and class are effectively identical:

struct DateStruct
{
    int year {};
    int month {};
    int day {};
};

class DateClass
{
public:
    int m_year {};
    int m_month {};
    int m_day {};
};

Note that the only significant difference is the public: keyword in the class. We will discuss the function of this keyword in the next lesson.

Warning

Just like with structs, one of the easiest mistakes to make in C++ is to forget the semicolon at the end of a class declaration. This will cause a compiler error on the next line of code. Modern compilers like Visual Studio 2010 will give you an indication that you may have forgotten a semicolon, but older or less sophisticated compilers may not, which can make the actual error hard to find.

Class (and struct) definitions are like a blueprint – they describe what the resulting object will look like, but they do not actually create the object. To actually create an object of the class, a variable of that class type must be defined:

DateClass today { 2020, 10, 14 }; // declare a variable of class DateClass

A reminder

Initialize the member variables of a class at the point of declaration.

Member Functions

Best practice

Name your classes starting with a capital letter.

With normal non-member functions, a function can’t call a function that’s defined “below” it (without a forward declaration):

void x()
{
// You can't call y() from here unless the compiler has already seen a forward declaration for y()
}

void y()
{
}

With member functions, this limitation doesn’t apply:

class foo
{
public:
     void x() { y(); } // okay to call y() here, even though y() isn't defined until later in this class
     void y() { };
};

Member types

A note about structs in C++

Best practice

Use the struct keyword for data-only structures. Use the class keyword for objects that have both data and functions.

You have already been using classes without knowing it

It turns out that the C++ standard library is full of classes that have been created for your benefit. std::string, std::vector, and std::array are all class types! So when you create an object of any of these types, you’re instantiating a class object. And when you call a function using these objects, you’re calling a member function.

Conclusion

The class keyword lets us create a custom type in C++ that can contain both member variables and member functions. Classes form the basis for Object-oriented programming, and we’ll spend the rest of this chapter and many of the future chapters exploring all they have to offer!

13.3 — Public vs private access specifiers

Public and private members

The code outside of a struct or class is sometimes called the public: the public is only allowed to access the public members of a struct or class, which makes sense.

Access specifiers

Mixing access specifiers

A class can (and almost always does) use multiple access specifiers to set the access levels of each of its members. There is no limit to the number of access specifiers you can use in a class.

In general, member variables are usually made private, and member functions are usually made public. We’ll take a closer look at why in the next lesson.

Best practice

Make member variables private, and member functions public, unless you have a good reason not to.

Some programmers prefer to list private members first, because the public members typically use the private ones, so it makes sense to define the private ones first. However, a good counterargument is that users of the class don’t care about the private members, so the public ones should come first. Either way is fine.

Access controls work on a per-class basis

#include <iostream>

class DateClass // members are private by default
{
	int m_month {}; // private by default, can only be accessed by other members
	int m_day {}; // private by default, can only be accessed by other members
	int m_year {}; // private by default, can only be accessed by other members

public:
	void setDate(int month, int day, int year)
	{
		m_month = month;
		m_day = day;
		m_year = year;
	}

	void print()
	{
		std::cout << m_month << '/' << m_day << '/' << m_year;
	}

	// Note the addition of this function
	void copyFrom(const DateClass& d)
	{
		// Note that we can access the private members of d directly
		m_month = d.m_month;
		m_day = d.m_day;
		m_year = d.m_year;
	}
};

int main()
{
	DateClass date;
	date.setDate(10, 14, 2020); // okay, because setDate() is public

	DateClass copy {};
	copy.copyFrom(date); // okay, because copyFrom() is public
	copy.print();
	std::cout << '\n';

	return 0;
}

One nuance of C++ that is often missed or misunderstood is that access control works on a per-class basis, not a per-object basis. This means that when a function has access to the private members of a class, it can access the private members of any object of that class type that it can see.

In the above example, copyFrom() is a member of DateClass, which gives it access to the private members of DateClass. This means copyFrom() can not only directly access the private members of the implicit object it is operating on (copy), it also means it has direct access to the private members of DateClass parameter d! If parameter d were some other type, this would not be the case.

This can be particularly useful when we need to copy members from one object of a class to another object of the same class. We’ll also see this topic show up again when we talk about overloading operator<< to print members of a class in the next chapter.

Structs vs classes revisited

Now that we’ve talked about access specifiers, we can talk about the actual differences between a class and a struct in C++. A class defaults its members to private. A struct defaults its members to public.

That’s it!

(Okay, to be pedantic, there’s one more minor difference – structs inherit from other classes publicly and classes inherit privately. We’ll cover what this means in a future chapter, but this particular point is practically irrelevant since you should never rely on the defaults anyway).

13.4 — Access functions and encapsulation

Encapsulation

Note: The word encapsulation is also sometimes used to refer to the packaging of data and functions that work on that data together. We prefer to just call that object-oriented programming.

Benefit: encapsulated classes are easier to use and reduce the complexity of your programs

Benefit: encapsulated classes help protect your data and prevent misuse

Access functions

Best practice

Getters should return by value or const reference.

13.5 — Constructors

A constructor is a special kind of class member function that is automatically called when an object of that class is created. Constructors are typically used to initialize member variables of the class to appropriate user-provided values, or to do any setup steps necessary for the class to be used (e.g. open a file or database).

After a constructor executes, the object should be in a well-defined, usable state.

Unlike normal member functions, constructors have specific rules for how they must be named:

  1. Constructors must have the same name as the class (with the same capitalization)
  2. Constructors have no return type (not even void)

Default constructors and default initialization

Value-initialization

Best practice

Favor value-initialization over default-initialization for class objects.

Note that value initialization uses empty braces, not empty parenthesis:

Fraction f1 {}; // value initialization of Fraction f1
Fraction f2();  // forward declaration of function f2

Direct- and list-initialization using constructors with parameters

Best practice

Favor brace initialization to initialize class objects.

Copy initialization using equals with classes

Much like with fundamental variables, it’s also possible to initialize classes using copy initialization:

Fraction six = Fraction{ 6 }; // Copy initialize a Fraction, will call Fraction(6, 1)
Fraction seven = 7; // Copy initialize a Fraction.  The compiler will try to find a way to convert 7 to a Fraction, which will invoke the Fraction(7, 1) constructor.

However, we recommend you avoid this form of initialization with classes, as it may be less efficient. Although direct-initialization, list-initialization, and copy-initialization all work identically with fundamental types, copy-initialization does not work the same with classes (though the end-result is often the same). We’ll explore the differences in more detail in a future chapter.

Reducing your constructors

In the above two-constructor declaration of the Fraction class, the default constructor is actually somewhat redundant. We could simplify this class as follows:

#include <cassert>

class Fraction
{
private:
    int m_numerator {};
    int m_denominator {};

public:
    // Default constructor
    Fraction(int numerator=0, int denominator=1)
    {
        assert(denominator != 0);

        m_numerator = numerator;
        m_denominator = denominator;
    }

    int getNumerator() { return m_numerator; }
    int getDenominator() { return m_denominator; }
    double getValue() { return static_cast<double>(m_numerator) / m_denominator; }
};

Although this constructor is still a default constructor, it has now been defined in a way that it can accept one or two user-provided values as well.

Fraction zero; // will call Fraction(0, 1)
Fraction zero{}; // will call Fraction(0, 1)
Fraction six{ 6 }; // will call Fraction(6, 1)
Fraction fiveThirds{ 5, 3 }; // will call Fraction(5, 3)

When implementing your constructors, consider how you might keep the number of constructors down through smart defaulting of values.

A reminder about default parameters

If we want to be able to construct a Something with only a double, we’ll need to add a second (non-default) constructor:

class Something
{
public:
	// Default constructor
	Something(int n = 0, double d = 1.2) // allows us to construct a Something(int, double), Something(int), or Something()
	{
	}

	Something(double d)
	{
	}
};

int main()
{
	Something s1 { 1, 2.4 }; // calls Something(int, double)
	Something s2 { 1 }; // calls Something(int, double)
	Something s3 {}; // calls Something(int, double)

	Something s4 { 2.4 }; // calls Something(double)

	return 0;
}

An implicitly generated default constructor

Best practice

If you have constructors in your class and need a default constructor that does nothing (e.g. because all your members are initialized using non-static member initialization), use = default.

Classes containing class members

Constructor notes

Best practice

Always initialize all member variables in your objects.

Quiz time

Question #2

What happens if you don’t declare a default constructor?

Hide Solution

If you haven’t defined any other constructors, the compiler will create an empty public default constructor for you. This means your objects will be instantiable with no parameters. If you have defined other constructors (default or otherwise), the compiler will not create a default constructor for you. Assuming you haven’t provided a default constructor yourself, your objects will not be instantiable without arguments.

13.6 — Constructor member initializer lists

Member initializer lists

To solve this problem, C++ provides a method for initializing class member variables (rather than assigning values to them after they are created) via a member initializer list (often called a “member initialization list”). Do not confuse these with the similarly named initializer list that we can use to assign values to arrays.

Now let’s write the same code using an initialization list:

#include <iostream>

class Something
{
private:
    int m_value1 {};
    double m_value2 {};
    char m_value3 {};

public:
    Something() : m_value1{ 1 }, m_value2{ 2.2 }, m_value3{ 'c' } // Initialize our member variables
    {
    // No need for assignment here
    }

    void print()
    {
         std::cout << "Something(" << m_value1 << ", " << m_value2 << ", " << m_value3 << ")\n";
    }
};

int main()
{
    Something something{};
    something.print();
    return 0;
}

The member initializer list is inserted after the constructor parameters. It begins with a colon (

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Basic Object-oriented Programming___CH_13 的相关文章

随机推荐

  • 编程思想:面向对象和面向过程

    何谓面向对象 何谓面向过程 对于这编程界的两大思想 一直贯穿在我们学习和工作当中 我们知道面向过程和面向对象 但要让我们讲出来个所以然 又感觉是不知从何说起 最后可能也只会说出一句就是那样啦 你知道啦 而这种茫然 其实就是对这两大编程思想的
  • linux系统在当前目录下创建子目录,linux根目录下一级子目录

    linux上每一个文件都必须是在根文件系统下的某个路径或者是从根文件系统才可以访问 文件系统中只分为目录和文件 所有文件都是存放在目录下 root根目录用 表示 ls 查看根目录下的一级子目录 boot 存放系统启动相关的文件 如内核文件
  • 地址解析中文乱码

    function Params var pattern w a zA Z0 9 u4e00 u9fa5 ig params 定义正则表达式和一个空对象 decodeURIComponent window location href true
  • 【数据结构】包装类&简单认识泛型

    文章目录 1 包装类 1 1 基本数据类型和对应的包装类 1 2 装箱和拆箱 2 什么是泛型 3 引出泛型 3 1 语法 4 泛型类的使用 4 1 语法 4 2 示例 4 3 类型推导 Type Inference 5 泛型的上界 5 1
  • E9流程表单中动态自定义添加button js代码

  • 发现一款专为.NET WinForms开发的数据可视化图表库

    FastReport是快捷 轻量级报表工具 使用简单 功能丰富 性价比高 适合个人 中小型企业和项目使用 FastReport近期推出了全新专为 NET WinForms开发的数据可视化图表库FastReport Business Grap
  • echarts水波球特效(附带外边框进度条)

    echarts水波球特效 附带外边框进度条 1 话不多说先上效果图 2 这里用到了echarts的liquidFill图表 需要加载liquidFill插件 npm i echarts liquidFill save 3 在main js入
  • c++ vector容器函数说明

    构造函数 vector 默认构造函数 创建一个空的 vector 对象 vector size type n 创建一个包含 n 个元素的 vector 对象 每个元素的值都是类型的默认值 vector size type n const v
  • yii2-admin扩展自定义目录

    yii2 admin文件如下 仓库地址 https github com mdmsoft yii2 admin tree master 复制yii2 admin文件至自定义目录 比如我就复制到了common rbac目录 在配置文件comm
  • 如何动态调试Python的第三方库

    注意 本文方法仅限于调试安装时附带py源码的库 如sklearn 引入 用sklearn中的sklearn feature extraction text TfidfTransformer来获取TF特征 但发现sklearn的计算结果与我手
  • 使用变量时,单引号、双引号、反向单引号的区别

    单引号 双引号 反向单引号的区别 1 单引号 当变量内容用单引号 时 对里面的命令echo和变量USER都无法识别 只识别成字符串本身 称之为强引用 2 双引号 当变量内容用双引号 时 是不能识别出里面的命令echo的 但可识别出里面的变量
  • 码农干货系列【1】--方向包围盒(OBB)碰撞检测

    码农干货系列 1 方向包围盒 OBB 碰撞检测 2012 06 07 11 40 by 当耐特 9251 阅读 20 评论 收藏 编辑 干货 最近一直在删文章 不是要关博洗手什么的 而是被删的文章没有达到 干货 的标准 干货的反义词是水货
  • tensorflow命令行参数之boolean使用的说明(踩坑指南)

    问题描述 简而言之就是 参数输出值与命令行输入值不一致 比如我们有个main py 具体代码如下 import tensorflow as tf tf app flags DEFINE boolean if gpu True if use
  • HTML网页链接

    1 链接的基本认识 所谓链接 是指一个页面之向一个目标的链接关系 链接地址不仅仅是一个页面地址 也可能是一个文件地址或者邮箱地址 文本链接 图像链接 邮箱地址的写法 a href 2 同一页面中快速查找消息 通过设置a标签中src内容的 来
  • Flutter Timer 定时器

    Flutter Timer 定时器 引包 Timer 类存在于dart async内 所以我们需要先导入 import dart async 使用场景 1 回调只需要一次 2 回调多次 例 回调一次 Duration timeout Dur
  • 玩转Matplotlib绘制柱形图

    使用Python Matplotlib模块中的bar 函数绘制柱形图 可视化直观地对比数据 代码如下 import pandas as pd import numpy as np import matplotlib pyplot as pl
  • **开启我的51单片机学习之路,基于51单片机数码管显示电子时钟**

    1 此款电子时钟 时间可以设置 有4个按键 一个功能按键 一个加 一个减 一个确定按键 2 按下功能按键一下 时钟走时停止 时两位数码管闪烁 代表此时再调时状态 这个时候加减按键就可以调整时钟 再次按下功能按键 就可以调整分钟 再按一下功能
  • Linux 音视频开发杂记之三-wayland环境搭建

    一 环境安装与运行测试程序 安装测试程序 Weston是Wayland提供的示例程序 sudo apt install weston 运行Weston 如果设置了 DISPLAY 那么Weston将在一个窗口中的X下运行 并从X中获取输入
  • 关于opencv更改摄像头参数(帧率,分辨率,曝光度……)的几个问题

    1 适用于VideoCapture打开的摄像头 VideoCapture capture 0 设置摄像头参数 不要随意修改 capture set CV CAP PROP FRAME WIDTH 1080 宽度 capture set CV
  • Basic Object-oriented Programming___CH_13

    13 1 Welcome to object oriented programming Object oriented programming OOP provides us with the ability to create objec