เป็นความคิดที่ไม่ดีหรือไม่ที่ทำให้วิธีการเรียนที่ผ่านการเรียนตัวแปรหรือไม่


14

นี่คือสิ่งที่ฉันหมายถึง:

class MyClass {
    int arr1[100];
    int arr2[100];
    int len = 100;

    void add(int* x1, int* x2, int size) {
        for (int i = 0; i < size; i++) {
            x1[i] += x2[i];
        }
    }
};

int main() {
    MyClass myInstance;

    // Fill the arrays...

    myInstance.add(myInstance.arr1, myInstance.arr2, myInstance.len);
}

addสามารถเข้าถึงตัวแปรทั้งหมดที่ต้องการได้แล้วเนื่องจากเป็นวิธีการเรียนดังนั้นนี่เป็นความคิดที่ไม่ดีหรือไม่? มีเหตุผลทำไมฉันควรหรือไม่ควรทำเช่นนี้?


13
หากคุณรู้สึกอยากทำสิ่งนี้มันเป็นการออกแบบที่ไม่ดี คุณสมบัติของคลาสอาจอยู่ในที่อื่น
bitsoflogic

11
ข้อมูลโค้ดมีขนาดเล็กเกินไป ระดับของ abstractions แบบผสมนี้ไม่ได้เกิดขึ้นในตัวอย่างของขนาดนี้ คุณสามารถแก้ไขได้ด้วยข้อความที่ดีในการพิจารณาการออกแบบ
Joshua

16
ฉันไม่เข้าใจจริงๆว่าทำไมคุณถึงทำอย่างนี้ คุณได้อะไร ทำไมไม่เพียงแค่มีวิธีไม่หาเรื่องaddที่ทำงานบน internals โดยตรง ทำไมล่ะ
Ian Kemp

2
@IanKemp หรือมีaddเมธอดที่รับอาร์กิวเมนต์ แต่ไม่มีอยู่ในชั้นเรียน เป็นฟังก์ชั่นที่บริสุทธิ์สำหรับการเพิ่มสองอาร์เรย์เข้าด้วยกัน
Kevin

วิธีการเพิ่มจะเพิ่ม arr1 และ arr2 เสมอหรือสามารถใช้เพื่อเพิ่มอะไรก็ได้หรือไม่
user253751

คำตอบ:


33

อาจมีบางสิ่งในชั้นเรียนที่ฉันจะทำแตกต่างกัน แต่เพื่อตอบคำถามโดยตรงคำตอบของฉันก็คือ

ใช่มันเป็นความคิดที่ไม่ดี

เหตุผลหลักของฉันคือคุณไม่สามารถควบคุมสิ่งที่ส่งผ่านไปยังaddฟังก์ชันได้ แน่นอนว่าคุณหวังว่ามันจะเป็นหนึ่งในอาร์เรย์สมาชิก แต่จะเกิดอะไรขึ้นถ้ามีคนส่งอาร์เรย์ต่าง ๆ ที่มีขนาดเล็กกว่า 100 หรือคุณผ่านความยาวมากกว่า 100

สิ่งที่เกิดขึ้นคือคุณได้สร้างความเป็นไปได้ของการบัฟเฟอร์ที่มากเกินไป และนั่นเป็นสิ่งที่ไม่ดีรอบตัว

เพื่อตอบคำถามที่ชัดเจนมากขึ้น (ถึงฉัน):

  1. คุณกำลังผสมอาร์เรย์สไตล์ C กับ C ++ ฉันไม่ใช่กูรู C ++ แต่ฉันรู้ว่า C ++ มีวิธีจัดการอาร์เรย์ที่ดีกว่า (ปลอดภัยกว่า)

  2. ถ้าคลาสมีตัวแปรสมาชิกอยู่แล้วทำไมคุณต้องผ่านมันไป? นี่เป็นคำถามทางสถาปัตยกรรมมากกว่า

คนอื่นที่มีประสบการณ์ C ++ มากขึ้น (ฉันหยุดใช้งานเมื่อ 10 หรือ 15 ปีที่แล้ว) อาจมีวิธีอธิบายปัญหาที่ละเอียดกว่าและอาจเกิดปัญหามากขึ้นเช่นกัน


แน่นอนว่าvector<int>จะเหมาะสมกว่า ความยาวก็จะไร้ประโยชน์ อีกทางเลือกหนึ่งconst int lenเนื่องจากอาร์เรย์ความยาวผันแปรไม่ได้เป็นส่วนหนึ่งของมาตรฐาน C ++ (แม้ว่าคอมไพเลอร์บางตัวจะสนับสนุนเพราะเป็นคุณสมบัติเสริมใน C)
Christophe

5
@Christophe ผู้เขียนใช้อาร์เรย์ขนาดคงที่ซึ่งควรถูกแทนที่ด้วยstd::arrayซึ่งไม่สลายตัวเมื่อส่งไปยังพารามิเตอร์มีวิธีที่สะดวกกว่าในการรับขนาดสามารถคัดลอกได้และเข้าถึงการตรวจสอบขอบเขตเพิ่มเติมในขณะที่ไม่มีค่าใช้จ่ายเกิน อาร์เรย์สไตล์ C
Cubic

1
@Cubic: เมื่อใดก็ตามที่ฉันเห็นรหัสประกาศอาร์เรย์ที่มีขนาดคงที่ตามอำเภอใจ (เช่น 100) ฉันค่อนข้างมั่นใจว่าผู้เขียนเป็นผู้เริ่มต้นที่ไม่มีความคิดเกี่ยวกับความหมายของเรื่องไร้สาระนี้และเป็นความคิดที่ดีที่จะแนะนำให้เขา / เธอ การเปลี่ยนการออกแบบนี้เป็นสิ่งที่มีขนาดตัวแปร
Doc Brown

อาร์เรย์นั้นใช้ได้
การแข่งขัน Lightness กับโมนิก้า

... สำหรับผู้ที่ไม่เข้าใจในสิ่งที่ฉันหมายถึงดูZero One Infinity Rule
Doc Brown

48

การเรียกเมธอดคลาสที่มีตัวแปรคลาสบางตัวนั้นไม่ได้เลวร้ายเสมอไป แต่การทำเช่นนี้จากนอกห้องเรียนเป็นความคิดที่แย่มากและแนะนำข้อบกพร่องพื้นฐานในการออกแบบ OO ของคุณคือไม่มีการห่อหุ้มที่เหมาะสม:

  • รหัสใด ๆ ที่ใช้คลาสของคุณจะต้องรู้ว่าlenเป็นความยาวของอาร์เรย์และใช้อย่างสม่ำเสมอ นี้ไปกับหลักการของความรู้น้อย การพึ่งพารายละเอียดภายในของชั้นเรียนนั้นมีข้อผิดพลาดและเสี่ยงมาก
  • สิ่งนี้จะทำให้วิวัฒนาการของคลาสนั้นยากมาก (เช่นถ้าวันหนึ่งคุณต้องการเปลี่ยนอาร์เรย์ดั้งเดิมและlenทันสมัยกว่าstd::vector<int>) เนื่องจากมันจะทำให้คุณต้องเปลี่ยนรหัสทั้งหมดโดยใช้คลาสของคุณ
  • ส่วนใดของรหัสสามารถสร้างความหายนะในMyClassวัตถุของคุณโดยทำให้ตัวแปรสาธารณะบางส่วนเสียหายโดยไม่เคารพกฎ (เรียกว่าค่าคงที่ของคลาส )
  • ในที่สุดวิธีการในความเป็นจริงเป็นอิสระจากชั้นเรียนเพราะมันจะทำงานเฉพาะกับพารามิเตอร์และขึ้นอยู่กับองค์ประกอบระดับอื่น ๆ วิธีการนี้อาจเป็นฟังก์ชั่นอิสระนอกชั้นเรียนได้เป็นอย่างดี หรืออย่างน้อยก็เป็นวิธีการคงที่

คุณควร refactor รหัสของคุณและ:

  • ทำตัวแปรคลาสของคุณprivateหรือprotectedถ้าไม่มีเหตุผลที่ดีที่จะทำ
  • การออกแบบวิธีการสาธารณะของคุณกับการดำเนินการที่จะดำเนินการในชั้นเรียน (เช่น object.action(additional parameters for the action))
  • หากหลังจากการเปลี่ยนโครงสร้างใหม่นี้คุณยังคงมีวิธีการเรียนบางอย่างที่ต้องถูกเรียกด้วยตัวแปรระดับทำให้พวกเขาได้รับการปกป้องหรือเป็นส่วนตัวหลังจากตรวจสอบแล้วว่าพวกเขาเป็นฟังก์ชันอรรถประโยชน์ที่สนับสนุนวิธีการสาธารณะ

7

เมื่อความตั้งใจของการออกแบบนี้คือการที่คุณต้องการที่จะสามารถนำมาใช้วิธีการนี้สำหรับข้อมูลที่ไม่ได้มาจากอินสแตนซ์ชั้นนี้แล้วคุณอาจต้องการที่จะประกาศว่ามันเป็นวิธีการstatic

วิธีการคงที่ไม่ได้เป็นของอินสแตนซ์ของส่วนใดส่วนหนึ่งของชั้นเรียน มันค่อนข้างเป็นวิธีการเรียน เนื่องจากวิธีการแบบสแตติกไม่ได้ทำงานในบริบทของอินสแตนซ์เฉพาะของคลาสพวกเขาสามารถเข้าถึงตัวแปรสมาชิกที่ประกาศเป็นสแตติกเท่านั้น พิจารณาว่าวิธีการของคุณaddไม่ได้อ้างถึงตัวแปรสมาชิกใด ๆ ที่ประกาศในMyClassมันเป็นตัวเลือกที่ดีสำหรับการประกาศว่าเป็นแบบคงที่

อย่างไรก็ตามปัญหาความปลอดภัยที่กล่าวถึงโดยคำตอบอื่น ๆ ที่ถูกต้อง: คุณไม่ได้ตรวจสอบว่าทั้งสองอาร์เรย์มีความlenยาวอย่างน้อย หากคุณต้องการเขียน C ++ ที่ทันสมัยและแข็งแกร่งคุณควรหลีกเลี่ยงการใช้อาร์เรย์ ใช้คลาสstd::vectorแทนทุกครั้งที่ทำได้ ตรงกันข้ามกับอาร์เรย์ปกติเวกเตอร์รู้ขนาดของตัวเอง ดังนั้นคุณไม่จำเป็นต้องติดตามขนาดตัวเอง วิธีการของพวกเขาส่วนใหญ่ (ไม่ใช่ทั้งหมด!) ทำการตรวจสอบที่ถูกผูกไว้โดยอัตโนมัติซึ่งรับประกันได้ว่าคุณจะได้รับข้อยกเว้นเมื่อคุณอ่านหรือเขียนในตอนท้าย การเข้าถึงอาร์เรย์ปกติไม่ได้ทำการตรวจสอบอย่างถูกต้องซึ่งส่งผลให้เกิด segfault ที่ดีที่สุดและอาจทำให้เกิดช่องโหว่หน่วยความจำล้นในช่องโหว่ที่แย่ลง


1

วิธีการในชั้นเรียนจัดการกับข้อมูลในแค็ปซูลคลาส ในตัวอย่างของคุณไม่มีเหตุผลที่วิธีการเพิ่มจะมีพารามิเตอร์ใด ๆ เพียงแค่ใช้คุณสมบัติของคลาส


0

ผ่าน (บางส่วน) วัตถุไปยังฟังก์ชันสมาชิกไม่เป็นไร เมื่อใช้งานฟังก์ชั่นของคุณคุณจะต้องตระหนักถึงนามแฝงที่เป็นไปได้และผลที่ตามมา

แน่นอนสัญญาอาจทำให้นามแฝงบางประเภทไม่ได้กำหนดแม้ว่าภาษาจะรองรับก็ตาม

ตัวอย่างที่โดดเด่น:

  • การกำหนดตัวเอง นี่ควรจะเป็น a-แพงอาจไม่มี -op อย่าลบล้างกรณีทั่วไปให้น้อยที่สุด
    การย้ายตัวเองในอีกด้านหนึ่งในขณะที่มันไม่ควรระเบิดในหน้าของคุณไม่จำเป็นต้องเป็นแบบไม่มี op
  • การแทรกสำเนาขององค์ประกอบในคอนเทนเนอร์ลงในคอนเทนเนอร์ v.push_back(v[2]);สิ่งที่ชอบ
  • std::copy() สมมติว่าปลายทางไม่ได้เริ่มต้นภายในแหล่งที่มา

แต่ตัวอย่างของคุณมีปัญหาแตกต่างกัน:

  • สมาชิกที่ฟังก์ชั่นไม่ได้ใช้ใด ๆ ของ priviliges thisตนมิได้หมายถึง มันทำหน้าที่เหมือนฟังก์ชั่นยูทิลิตี้ฟรีซึ่งอยู่ผิดที่
  • ชั้นเรียนของคุณไม่พยายามที่จะนำเสนอชนิดของใด ๆที่เป็นนามธรรม สอดคล้องกับที่ก็ไม่ได้พยายามที่จะห่อหุ้มอวัยวะภายในของตนมิได้รักษาใด ๆคงที่ มันเป็นองค์ประกอบที่ไม่เจาะจง

โดยสรุป:ใช่ตัวอย่างนี้ไม่ดี แต่ไม่ใช่เพราะฟังก์ชั่นสมาชิกได้รับการส่งผ่านวัตถุสมาชิก


0

การทำเช่นนี้เป็นความคิดที่ไม่ดีด้วยเหตุผลที่ดีหลายประการ แต่ฉันไม่แน่ใจว่าทั้งหมดนั้นเป็นส่วนสำคัญสำหรับคำถามของคุณ:

  • การมีตัวแปรคลาส (ตัวแปรจริงไม่ใช่ค่าคงที่) เป็นสิ่งที่ควรหลีกเลี่ยงหากเป็นไปได้และควรกระตุ้นให้เกิดการไตร่ตรองอย่างจริงจังว่าคุณต้องการมันหรือไม่
  • การออกจากการเข้าถึงเพื่อเขียนตัวแปรคลาสเหล่านี้เปิดให้ทุกคนนั้นแย่มากในระดับสากล
  • วิธีการเรียนของคุณสิ้นสุดลงโดยไม่เกี่ยวข้องกับชั้นเรียนของคุณ จริงๆแล้วมันคือฟังก์ชั่นที่เพิ่มค่าของอาร์เรย์หนึ่งให้กับค่าของอีกอาร์เรย์หนึ่ง ฟังก์ชันประเภทนี้ควรเป็นฟังก์ชันในภาษาที่มีฟังก์ชั่นและควรเป็นวิธีการคงที่ของ "คลาสปลอม" (เช่นชุดของฟังก์ชันที่ไม่มีข้อมูลหรือพฤติกรรมของวัตถุ) ในภาษาที่ไม่มีฟังก์ชัน
  • อีกวิธีหนึ่งคือลบพารามิเตอร์เหล่านี้และเปลี่ยนเป็นวิธีการเรียนจริง แต่ก็ยังคงไม่ดีสำหรับเหตุผลที่กล่าวถึงก่อนหน้านี้
  • ทำไมต้องใช้อาร์เรย์ vector<int>จะทำให้ชีวิตของคุณง่ายขึ้น
  • หากตัวอย่างนี้แสดงถึงการออกแบบของคุณให้ลองวางข้อมูลของคุณในวัตถุไม่ใช่ในคลาสและใช้ตัวสร้างสำหรับวัตถุเหล่านี้ในแบบที่ไม่ต้องการเปลี่ยนค่าของข้อมูลวัตถุหลังจากการสร้าง

0

นี่คือวิธีการ "C พร้อมคลาส" แบบคลาสสิกกับ C ++ อันที่จริงแล้วนี่ไม่ใช่สิ่งที่โปรแกรมเมอร์ซีพลัสพลัสจะเขียน สำหรับการใช้อาร์เรย์ C ดิบนั้นไม่น่าเป็นไปได้ในระดับสากลยกเว้นว่าคุณกำลังใช้ไลบรารีคอนเทนเนอร์

สิ่งนี้จะเหมาะสมกว่า:

// Don't forget to compile with -std=c++17
#include <iostream>
using std::cout; // This style is less common, but I prefer it
using std::endl; // I'm sure it'll incite some strongly opinionated comments

#include <array>
using std::array;

#include <algorithm>

#include <vector>
using std::vector;


class MyClass {
public: // Begin lazy for brevity; don't do this
    std::array<int, 5> arr1 = {1, 2, 3, 4, 5};
    std::array<int, 5> arr2 = {10, 10, 10, 10, 10};
};

void elementwise_add_assign(
    std::array<int, 5>& lhs,
    const std::array<int, 5>& rhs
) {
    std::transform(
        lhs.begin(), lhs.end(), rhs.begin(),
        lhs.begin(),
        [](auto& l, const auto& r) -> int {
            return l + r;
        });
}

int main() {
    MyClass foo{};

    elementwise_add_assign(foo.arr1, foo.arr2);

    for(auto const& value: foo.arr1) {
        cout << value << endl; // arr1 values have been added to
    }

    for(auto const& value: foo.arr2) {
        cout << value << endl; // arr2 values remain unaltered
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.